Django, jquery.formset and Select2 issues - javascript

Using Django 1.6, django-select2 (latest) and jquery.formset.js (latest), I'm struggling with something that should be quite simple. Essentially when I use the add formset capability provided by jquery.formset.js the new formset is missing the select field provided by django-select2, although the rest of the formset renders fine.
It's a fairly basic set up:
class PartNumber(models.Model):
name = models.CharField("Description", max_length=100)
supplier_part_number = models.CharField(max_length=30, unique=True, blank=True, null=True)
class PurchaseOrder(models.Model):
po_number = models.CharField('PO number', max_length=10, unique=True)
ordered_date = models.DateField(default=today)
class PurchaseOrderPart(models.Model):
part_number = models.ForeignKey(PartNumber, related_name='purchases')
po_number = models.ForeignKey(PurchaseOrder, related_name='partslist')
delivery_date = models.DateField(null=True, blank=True)
qty_ordered = models.IntegerField('Quantity ordered',validators=[MinValueValidator(1)])
cost = models.DecimalField('Unit Cost', max_digits=10,decimal_places=2,blank=True,null=True)
I have the create view of a PurchaseOrder having PurchaseOrderParts as an inline_formset:
class PurchaseOrderPartForm(forms.ModelForm):
part_numbers = PartNumberChoices()
class Meta:
fields = ('part_numbers', 'delivery_date', 'qty_ordered', 'cost')
model = PurchaseOrderPart
widgets={
'part_numbers': forms.Select(attrs={'class':'form-control'}),
'delivery_date': CalendarWidget(attrs={'class':'input-append form-control'}),
'qty_ordered': forms.NumberInput(attrs={'class':'form-control'}),
'cost': forms.NumberInput(attrs={'class':'form-control'}),
}
POPartFormset = inlineformset_factory(PurchaseOrder, PurchaseOrderPart, form=PurchaseOrderPartForm, extra=1, can_delete=True)
And I'm using jquery.formset.js so that there are "add" and "remove" buttons on the PurchaseOrder create view, so that any number of PurchaseOrderParts might be added (any one PO might have from 1 to infinite parts attached, in reality more likely to be < 10).
That was working fine.
But the PartNumber fk on a PurchaseOrderPart has a huge list (~2500 items) so the select list is inconvenient.
I installed django-select2 and was pretty impressed at how easily it was to get running:
class PurchaseOrderPartForm(forms.ModelForm):
part_numbers = PartNumberChoices()
class Meta:
fields = ('part_numbers', 'delivery_date', 'qty_ordered', 'cost')
model = PurchaseOrderPart
widgets={
'part_numbers': django_select2.AutoHeavySelect2Widget(),
'delivery_date': CalendarWidget(attrs={'class':'input-append form-control'}),
'qty_ordered': forms.NumberInput(attrs={'class':'form-control'}),
'cost': forms.NumberInput(attrs={'class':'form-control'}),
}
This is when we have problems. It all works until you use the jquery.formset.js to add a new formset - it renders perfectly but fails to include the django-select2 select.
In particular, the new formset is missing this code:
<div class="select2-container select2-container-active" id="s2id_id_partslist-1-part_numbers"> <span class="select2-chosen"> </span><abbr class="select2-search-choice-close"></abbr> <span class="select2-arrow"><b></b></span><input class="select2-focusser select2-offscreen" type="text" id="s2id_autogen5"></div>
How might I go about force inserting the select2-container?

This is a known bug of django-select2 that has been fixed in a very recent merge.
When you are saying that you are running the latest version, do you mean from github or from PIP ?
Either way, you might be interrested in this:
https://github.com/applegrew/django-select2/pull/127

Related

Not getting redirected back to the updated blog post in django

i have a single Blog,i am trying to only have a detailview,but when i add new post it will only show the pagination,but the post will already be added to my blogpost not until i go to the blog home page,even when i search for a blogpost e.g http://127.0.0.1:8000/pages/blog/22/?= i wont get what i want
##views.py
class BlogDetailView(DetailView):
model = Blog
template_name = 'pages/blog.html'
context_object_name = 'blog'
ordering = ['-timestamp']
##urls.py
path('blog/', blog, name='blog'),
path('blog/<int:pk>/', BlogDetailView.as_view(), name='blog'),
i created a listview and detailview separately, with a diffrent URL
class BlogListView(ListView):
model = Blog
template_name = 'pages/blog.html' # <app>/<model>_<viewtype>.html
context_object_name = 'blog_post'
ordering = ['-timestamp']
paginate_by = 5
class BlogDetailView(DetailView):
model = Blog

dynamic Backgrounds for django using image upload

I've looked around and haven't found a satisfactory solution. I am trying to uploaded an image via the Django admin interface using models and then call that uploaded image to be used as the background for a div .i.e - I create football called Salford City, I upload a picture of the team. I will have several teams. If I choose Salford then the background changes, if I choose Brighton Albion then the background changes. I have it working using javascript and hard coded cities but I would like to be able to do it dynamically without have to change base coding to enable easy scaling. Current code is as follows:
Models.py:
class City(models.Model):
index = models.CharField(unique=True, max_length=250, null=False, blank = False, verbose_name = 'City Inner Search Name(lowercase)')
landing_page_description = models.CharField(max_length=5000, null=True, blank = True, verbose_name = 'City Description')
name = models.CharField(max_length=250, null=False, blank = False, verbose_name = 'City Common Name')
country = models.ForeignKey(Country, null=True, blank=True)
image = models.ImageField(upload_to="images/", blank=True, null=True, verbose_name= 'City Image')
The images are stored in media_files/images
The html:
<div id="main" class="container-fluid citybackground">
<!-- <img src="{{ MEDIA_URL}}{{ City.image.url }}"> -->
AS you can see I tried multiple options. The commented out section was a method I tried.
The css:
.citybackground
{
background-image: url( "{% images {{City.image.url}} %}");
background-repeat:no-repeat;
background-size:100%;
}
The views.py:
def SetCitySession(request):
if request.method == "POST":
request.session['city-name'] = request.POST['cityName']
request.session['city-id'] = request.POST['cityId']
return JsonResponse({})
def GetCitySession(request):
if request.method == "GET":
cityName = request.session['city-name']
cityId = request.session['city-id']
context = {
"cityName": cityName,
"cityId": cityId
}
return JsonResponse(context)
This is currently working hard coded javascript which I want to stop using since it isn't dynamic.
function changeBackground(city){
var urlBackground;
switch(city){
case "Manchester":
urlBackground = "/static/img/fondo_main.jpg";
break;
case "Liverpool":
urlBackground = "/static/img/liverpool_background.jpg";
break;
case "London":
urlBackground = "/static/img/london_background.jpg";
break;
}
$("#main").css("background-image", "url('{% if city.image %}{{ city.image.url }}{% else %}{"+ urlBackground + "}{% endif %}')";
}
Thanks ya'll
Simply write it as:
background-image: url({{ city.image.url }});
Use inline style for background image
<div id="main" class="container-fluid citybackground" style="background-image: url({{City.image.url}})">
I wrote this question when I was completely new to Django, finishing off an existing project whilst not fully understanding how the system worked. The answer by #yusuf.oguntula is correct in a manner. In the the question I neglected to add the view that actually showed the background image and rather gave the views which where used to retrieve and set session data on the server. Secondly, the actual view did not state which city it was since it had not retrieved the city from django. At the time the city was set by javascript but there was no query set or object retrieval within the view itself so as to allow interaction with python syntax. This meant even though the solution suggested by #yusuf.oguntula was correct it did not work due to the missing query set. I fixed this first by retrieving the appropriate city to the HomepageView in views.py like this:
if 'city-name' not in request.session:
cityName='Madrid'
request.session['city-name'] = request.POST.get('cityName')
else:
cityName = request.session['city-name']
city = get_object_or_404(City, name=cityName)
and then used #yusuf.oguntula 's solution in my html. It worked perfectly.
Add inline CSS to the element you want to have a background image. For example,
<div style="background: url('{{ home.image.url }}');">
</div>
This will work for sure

Mandatory slider in oTree/django

I want to use oTree as an alternative for conducting experiments. For this purpose I am looking for a possibility to include mandatory slider questions in forms, i. e. sliders you are required to move before you are able to proceed to the next question. As a start I tried to modify oTrees survey template to achieve a solution for future usage but wasn't able to integrate common approaches like a fieldtracker into the project.
Here are two modified (yet currently after a number of unsuccessful try-outs not really functioning) versions of the models.py and views.py files which give a hint in which direction I want to go. Is there a way to get this to work?
# -*- coding: utf-8 -*-
## models.py
# <standard imports>
from __future__ import division
from django.db import models
from django_countries.fields import CountryField
from model_utils import FieldTracker,
from otree import widgets
from otree.constants import BaseConstants
from otree.db import models
from otree.models import BaseSubsession, BaseGroup, BasePlayer
class Constants(BaseConstants):
name_in_url = 'survey'
players_per_group = None
num_rounds = 1
class Subsession(BaseSubsession):
pass
class Group(BaseGroup):
pass
class Player(BasePlayer):
def set_payoff(self):
"""Calculate payoff, which is zero for the survey"""
self.payoff = 0
q_country = CountryField(
verbose_name='What is your country of citizenship?')
q_age = IntegerFielder(verbose_name='What is your age?',
min=13, max=125,
initial=25,
widget=widgets.SliderInput())
q_gender = models.CharField(initial=None,
choices=['Male', 'Female'],
verbose_name='What is your gender?',
widget=widgets.RadioSelect())
tracker = FieldTracker()
crt_bat = models.PositiveIntegerField()
crt_widget = models.PositiveIntegerField()
crt_lake = models.PositiveIntegerField()
Here comes the second file:
# -*- coding: utf-8 -*-
##views.py
from __future__ import division
from . import models
from ._builtin import Page, WaitPage
from otree.common import Currency as c, currency_range
from .models import Constants, integerfieldcustom
class Demographics(Page):
form_model = models.Player
form_fields = ['q_country',
'q_age',
'q_gender']
check_age = q_age.tracker.has_changed()
def q_age_error_message(self, ):
if Demographics.check_age == False:
return 'You must move the slider before you can continue'
class CognitiveReflectionTest(Page):
form_model = models.Player
form_fields = ['crt_bat',
'crt_widget',
'crt_lake']
def before_next_page(self):
self.player.set_payoff()
page_sequence = [
Demographics,
CognitiveReflectionTest
]
Thanks in advance!
There are two ways of doing it: by using JS only, on the client's side, and by using Django at the server side.
The simple JS solution:
in the template add:
{% block scripts %}
<script>
var SliderTouched = false;
var selector = $('[data-slider] input[type="range"]');
selector.change(function() {
SliderTouched = true;
});
$( ".form" ).submit(function( event ) {
if (!SliderTouched){
event.preventDefault();}
});
</script>
{% endblock %}
So until the user triggers change event, the SliderTOuched var is set to False which prevents a form to be submitted. It is a compact way, but you have to deal with showing an error message to the user yourself.
=================
The longer server-side solution is the following:
in models.py define an additional field:
class Player(BasePlayer):
checkslider = models.IntegerField(blank=True)
in views.py in addition to your slider field pass also this extra field that will check that the slider was changed:
class MyPage(Page):
form_model = models.Player
form_fields = ['q_age', 'checkslider']
def checkslider_error_message(self, value):
if not value:
return 'Please make your decision using slider'
in template insert this hidden extra field to html:
<input type="hidden" name="checkslider" value="" id="id_checkslider"/>
and set this field to current slider value as soon as slider is changed:
{% block scripts %}
<script>
var selector = $('[data-slider] input[type="range"]');
selector.change(function() {
$('#id_checkslider').val(selector.val());
});
</script>
{% endblock %}
By default, Django assumes an input is required.
I think that means if you just remove the initial value, it will self-validate.
Also, you called something named "IntegerFielder()." Did you mean models.IntegerField() or is there an import that we're not seeing?
I suggest a slight modification to Philipp's answer.
The code above still triggers the error message if the participant touches the slider, but returns the slider to the default starting position.
To fix this, I used the following script:
{% block scripts %}
<script>
$('input[name=q_age]').on('input', function(){
$('#id_checkslider').val(1);
});
</script>
{% endblock %}
The code changes checkslider from None to 1 when the slider is touched, even if the participant sets the slider to the default starting position.

How to save Django "forms.ModelForm" in database after editing it content and passing a javascript variable to it's new entered field

I have a model look like this:
class filled(stateobject):
first = models.IPAddressField(verbose_name=_("First address"))
second = models.CharField(max_length=39, verbose_name=_("Second address")
Embedding these model into Django-forms by following code :
class hostCreateForm(forms.ModelForm):
class Meta:
model = filled
widgets = {
'user': forms.HiddenInput()
}
In views.py:
def address_create(request):
if required.method=='POST':
form = HostCreateForm(request.POST)
if form.is_valid():
host = form.save()
return redirect_to(request, url=host.get_absolute_url())
extra_context = {
'form': HostCreateForm(initial={'user': request.user.pk})
}
return direct_to_template(request, 'networks/Address_form.html', extra_context)
In :Address_form.html
{{ form.as_p }}
When i run above code's it shows me a form containing two fields i.e. first and second. I want to edit this form and add a new char field by named GMT, So that i am able to pass a following javascript variable i.e gmtHours in this new created field
var d = new Date()
var gmtHours = -d.getTimezoneOffset()/60;
Finally when user's click on sumbit button at template it save a form content i.e.
1. first
2. second
3. newly created gmtHours
I am newbie in Django-model. Want your help :)
Add a hidden field to your ModelForm that will hold the date:
class hostCreateForm(forms.ModelForm):
class Meta:
model = filled
widgets = {
'user': forms.HiddenInput()
}
# additional hidden field
datetime = forms.DatetimeField(widget=forms.HiddenInput)
You can then assign it a value in the javascript.

Django and Ajax/Javascript

I have the following model layout:
class A(models.model):
options = models.ManyToManyField(OptionSet, blank=True, null=True)
values = models.ManyToManyField(Value, blank=True, null=True)
class OptionSet(models.model):
name = models.TextField(unique=True)
values = models.ManyToManyField(Value)
def __unicode__(self):
return '%s' % self.name
class Value(models.Model):
name = models.TextField()
key = models.ForeignKey(Key, related_name='values')
class Key(models.Model):
name = models.TextField(unique=True)
And my forms.py looks like this:
class A_Form(ModelForm):
values = forms.ModelMultipleChoiceField(queryset=Value.objects.all(), widget=CheckboxSelectMultiple, label="Einzelne Werte", required=False)
options = forms.ModelMultipleChoiceField(queryset=OptionSet.objects.all(), widget=CheckboxSelectMultiple, label="Optionen Sets", required=False)
Template:
<form action="." method="POST">{% csrf_token %}
{{ form.as_table }}
<input type="submit" value="Update"/>
</form>
I use that form with a generic update view!
I'm new to javascript/ajax to be honest never did something in javascript/ajax. What I want to do is on mouseover on the options name it should show all the values for that option set. How would one accomplish this?
You can use jquery's .post() method to send the name of the active option to django script(actually url on server).Then you can find out all the values using queries(I'm assuming you already know how to extract data from models).Then again you can use HttpResponse() to send the calculated values list back to your page.

Categories

Resources