I've been scanning through Django documentation, and Google search results, all afternoon and I'm still somewhat stuck in my attempt to create a dynamic form. I'm hoping I just need someone to nudge me in the right direction :-) I'm just starting to learn Django, so I'm still very much a beginner; however, I'm already an intermediate python user.
What I'm trying to do is create a dynamic form, where the user makes a selection from a drop-down menu, and based on that selection another part of the form will automatically update to display results relevant to the currently selected item, but from another database table.
I'll try and use a simplified version of the models from the Django tutorial to better illustrate what I'm trying to do:
# models.py
from django.db import models
class Poll(models.Model):
question = models.CharField(max_length=200)
class Choice(models.Model):
poll = models.ForeignKey(Poll)
choice = models.CharField(max_length=200)
So lets say I want to have something like a drop-down selection field, populated with the question from each Poll in the database. I also want to have a text-field, which displays the corresponding choices for the currently selected Poll, which will update on-the-fly whenever the user selects a different Pool. I've been able to figure this out by placing a button, and posting information back to the form; However, I'm trying to do this automatically as the user makes a selection. My view sort of looks something like this at the moment:
#view.py
from django import forms
from django.shortcuts import render_to_response
from myapp.models import Poll,Choice
class MyModelChoiceField(forms.ModelChoiceField):
def label_from_instance(self, obj):
return "%s" % obj.question
class PollSelectionForm(forms.Form):
polls = MyModelChoiceField( queryset=Poll.objects.all() )
class ChoiceResults(forms.Form):
def __init__(self, newid, *args, **kwargs):
super(ChoiceResults, self).__init__(*args, **kwargs)
self.fields['choice'] = forms.TextField( initial="" )
def main(request):
return render_to_response("myapp/index.html", {
"object": PollSelectionForm(),
"object2": ChoiceResults(),
})
My template is very simple, just something like
{{ object }}
{{ object2 }}
I'm sure the way I'm going about creating the forms is probably not the best either, so feel free to criticize that as well :-) As I mentioned, I've read solutions involving reposting the form, but I want this to happen on-the-fly... if I can repost transparently then that would be fine I guess. I've also seen libraries that will let you dynamically create forms, but that just seems like overkill.
Here is one approach - Django/jQuery Cascading Select Boxes?
You can create a new view that just renders json to a string,
and then trigger an event when you're done selecting from the first list which loads the data dynamically from that json.
I do a similar thing here, populating a form based on a selection in a drop down. Maybe this helps you.
Here is the model of the values used to pre-populate the form:
class OpmerkingenGebrek(models.Model):
opmerking = models.CharField(max_length=255)
advies = models.CharField(max_length=255)
urgentiecodering = models.CharField(max_length=2, choices=URGENTIE_CHOICES_2011)
bepaling = models.CharField(max_length=155,blank=True,null=True)
aard = models.CharField(max_length=3, choices=AARD_CHOICES)
The view that manages the form:
def manage_component(request,project_id,.....):
# get values for pre-populate
og = OpmerkingenGebrek.objects.all()
.........
formset = ComponentForm(request.POST,request.FILES)
.........
)))
return render_to_response(template, {
'formset':formset,
........
'og':og,
},context_instance=RequestContext(request))
The html the renders the form
{% extends "base.html" %}
{% block extra_js %}
<script type="text/javascript" src="/media/js/limitText.js"></script>
<script type="text/javascript" src="/media/js/getValueOpmerking.js"></script>
{% endblock %}
<form enctype="multipart/form-data" method="post" action="">
{{ formset.as_table }}
</form>
<p>Choose default values:</p>
<select id="default" onChange="getValue(this)">
{% for i in og %}
<option value="{{ i.opmerking }} | {{ i.advies }} | {{ i.urgentiecodering }} |
{{ i.aard }} | {{ i.bepaling }}">{{ i.opmerking }}</option>
{% endfor %}
</select>
The javascript that pre-populates the form:
function getValue(sel)
{
//get values
var opm = sel.options[sel.selectedIndex].value;
//split string to parts
var parts = opm.split("|");
// autofill form
var opmerking = document.getElementById("id_opmerking");
opmerking.value = parts[0];
var aanbeveling = document.getElementById("id_aanbeveling");
aanbeveling.value = parts[1];
var opt = document.getElementById("id_urgentie");
var urgentie = opt.selectedIndex;
for(var i=0;i<opt.length;i++){
if(opt.options[i].value == parts[2].split(' ').join('')){
opt.selectedIndex = i;
}};
var opt = document.getElementById("id_aard");
var aard = opt.selectedIndex;
for(var i=0;i<opt.length;i++){
if(opt.options[i].value == parts[3].split(' ').join('')){
opt.selectedIndex = i;
}};
var bepaling = document.getElementById("id_bepaling");
bepaling.value = parts[4];
};
Related
I have a class called Features in my models.py. In my html, I am displaying a list on the right that excludes two of these Features, one is the active feature that has been selected, the other is the most recently added since they are the main content of my page. The remaining Features in the list are displayed by date and do show what I am expecting.
Now, I want to single out the first, second and third Features (title only) in THAT list so I can place them in their own separate divs - because each has unique css styling. There are probably numerous ways of doing this, but I can't seem to figure any of them out.
This is a link to my project to give a better idea of what I want (basically trying to get the content in those colored boxes on the right.)
I'm just learning Django (and Python really), so thanks for your patience and help!
HTML
{% for f in past_features %}
{% if f.title != selected_feature.title %}
{% if f.title != latest_feature.title %}
<h1>{{ f.title }}</h1>
{% endif %}
{% endif %}
{% endfor %}
VIEWS
def feature_detail(request, pk):
selected_feature = get_object_or_404(Feature, pk=pk)
latest_feature = Feature.objects.order_by('-id')[0]
past_features = Feature.objects.order_by('-pub_date')
test = Feature.objects.last()
context = {'selected_feature': selected_feature,
'latest_feature': latest_feature,
'past_features': past_features,
'test': test}
return render(request, 'gp/feature_detail.html', context)
MODELS
class Feature(models.Model):
title = models.CharField(db_index=True, max_length=100, default='')
content = models.TextField(default='')
pub_date = models.DateTimeField(db_index=True, default=datetime.now, blank=True)
def __str__(self):
return self.title
def __iter__(self):
return [
self.id,
self.title ]
You can either store the first three Features in separate variables in your context or add checks to your template loop like {% if forloop.first %} or {% if forloop.counter == 2 %}.
If all you want is to not have the
selected_feature
latest_feature
these two records out of the past_features queryset, then you can use exclude on the past_features query and pass the id's of the selected_features and latest_feature objects.
The views.py would look like:
def feature_detail(request, pk):
selected_feature = get_object_or_404(Feature, pk=pk)
latest_feature = Feature.objects.order_by('-id')[0]
# Collect all the id's present in the latest_feature
excluded_ids = [record.pk for record in latest_feature]
excluded_ids.append(selected_feature.pk)
#This would only return the objects excluding the id present in the list
past_features = Feature.objects.order_by('-pub_date').exclude(id__in=excluded_ids)
test = Feature.objects.last()
context = {'selected_feature': selected_feature,
'latest_feature': latest_feature,
'past_features': past_features,
'test': test}
return render(request, 'gp/feature_detail.html', context)
Django provides a rich ORM and well documented, go through the Queryset options for further information.
For access to a specific object in Django templates see following example:
For access to first object you can use {{ students.0 }}
For access to second object you can use {{ students.1 }}
For access to a specific field for example firstname in object 4 you can use {{ students.3.firstname }}
For access to image field in second object you can use {{ students.1.photo.url }}
For access to id in first object you can use {{ students.0.id }}
I'm trying to use django-jfu to multiupload images, but I have a problem. I want to handle a foreign key dynamically (via url or something), but I can't think of anything.
I have the following models:
class Event(models.Model):
name = models.CharField(max_length=128)
class Picture(models.Model):
event = models.ForeignKey(Event)
image = models.ImageField(upload_to='media')
According to django-jfu, you have to specify a "upload" view to call from the template via template tag. This is my upload view:
#require_POST
def upload(request):
event = Event.objects.get(id=26)
file = upload_receive(request)
instance = Picture(image = file, event = event)
print instance
instance.save()
basename = os.path.basename(instance.image.path)
file_dict = {
'name' : basename,
'size' : file.size,
'url': settings.MEDIA_URL + basename,
'thumbnailUrl': settings.MEDIA_URL + basename,
'deleteUrl': reverse('jfu_delete', kwargs = { 'pk': instance.pk }),
'deleteType': 'POST',
}
return UploadResponse(request, file_dict)
Right now, as a test, it only saves pictures to event with id=26, but how can I handle it dynamically? This is the view and template where I'm calling the template tag:
view
def add_pictures_to_event(request, event_id):
return render(request, 'add_pictures_to_event.html')
template
{% extends 'base.html' %}
{% load staticfiles %}
{% load jfutags %}
{% block body %}
<div class="container">
<h2>Photo upload</h2>
{% jfu %}
</div>
{% endblock %}
As you can see, the view add_pictures_to_event, gets the request and the id of the event, but I cant seem to pass it to the upload view.
Any help would be appreciated.
I had the same question. I looked at different django versions of jQuery File Upload but stuck with Alem's jfu but with the changes from Thomas Willson to make it work in 1.9. My solution might not be the best but I could not find an other way.
I assume you already created an event and then add images to it.
media_upload_form.html is in my projects static directory. I used the UPLOAD_FORM_EXTRA block to add a hidden formfield with the current event_id:
{% block UPLOAD_FORM_EXTRA %}
<input type="hidden" name="currentevent" value="{{instance.pk}}">
{% endblock %}
I assume you have the view from the docs. I changed in the beginning of the uploadview:
file = upload_receive( request )
event_instance = get_object_or_404(Event, id=request.POST['currentevent'])
instance = Picture( file = file, event=event_instance)
instance.save()
It is probably against all django rules but it works. If anyone has a better solution I like to know too. FormSets maybe?
I'm trying to let the user select one 'thing' from a list (from the database), then go find other stuff in the database using that record. But I cannot get the selection info from the selection page.
I'll try to make this a pretty complete snapshot of the relevant code, but I may remove too much or leave too much in, sorry.
my models.py:
urlpatterns = patterns('',
url(r'^$', 'dblook.views.index', name='home'),
url(r'^dblook3/', 'dblook.views.try3', name='home2'),
url(r'^dblook4/', 'dblook.views.try4', name='home3'),
)
my dblook/models.py:
from django.db import models
class serial_number(models.Model):
def __unicode__(self):
return self.serialno
#return self.question
class Meta:
managed=False
db_table='serial_number'
sn_id = models.AutoField(primary_key=True)
serialno = models.CharField(max_length=128)
comment = models.ForeignKey(comment,null=True,db_column='comment')
my views.py (I will skip all the imports other than the database model import. If anyone really wants them I'll update with them)
from dblook.models import *
class SerialnoSelectForm(forms.Form):
serialno = forms.CharField(max_length=16)
selected = forms.BooleanField()
class serialform(ModelForm):
class Meta:
model = serial_number
exclude=('comment','sn_id')
selected = forms.BooleanField()
class snselect(forms.Form):
sno = forms.ChoiceField()
def try3(request):
if ( request.POST ):
output = "HEllo world, thanks for posting"
return HttpResponse(output)
else:
sslst = snselect(serial_number.objects.filter(serialno__startswith="A128").order_by('-serialno'))
t = loader.get_template('select_serialno.html')
c = Context({
'sslst': sslst,
})
c.update(csrf(request))
return HttpResponse(t.render(c))
def try4(request,dsn):
if ( request.POST ):
output = "HEllo world, thanks for posting to 4"
return HttpResponse(output)
else:
return HttpResponse("Error")
And my template (select_serialno.html) is:
<h1>Select a serial number</h1>
<ul>
<form method='post' action'='/dbtest4/{{serial_number.sn_id}}/showme'>
{% csrf_token %}
{% for sn in sslst %}
<input type="submit" name="sn.serialno" id="choice{{ forloop.counter }}" value="{{choice.id}}"/>
<label for="choice{{ forloop.counter }}">{{ sn.serialno }}</label><br/>
{% endfor %}
<input type="submit" value="data" />
</form>
When I go to dblook3, I get a nice list from the database of serial numbers, along with a button that, if I hit goes immediately to the dblook4 URL (in this case, its ALWAYS '/dbtest4//showme/' instead of something like '/dbtest4/3/showme/). Unfortunately, I cannot seem to have any way to tell what button they hit.
No matter WHAT I put in for the 'stuff' in <form method='post' action'='/dbtest/{{stuff}}/showme'>, it is always empty.
I also tried things like if( 'choice' in request.POST ): in try4 in veiws.py, but that didn't work either.
So, how do I get ANY information about what was selected from 'look3' over to 'look4'? I'll take just about anything... However, if you can explain why I'm doing that hopefully your answer will not only solve my problem, but help others understand...
(if the above looks pretty 'evolutionary' that's because I've been hacking on this for 3 days now...)
Thanks!
You need to POST the information to the look4 dblook form:
<form method='post' action'='{% url dblook.views.try4 %}'>
At the moment you have /dbtest/{{serial_number.sn_id}}/showme which doesn't make any sense. You don't have a serial_number variable in your context so I don't know where that comes from. You have def try4(request,dsn): as your view definition which suggests that you are trying to load information on the try4 view depending on what was selected fromt he try3 view (although I am guessing this as you haven't explained what you are trying to do). If that is the case, you need to do that based on the data passed via POST instead of url parameters. Something very vaguely like the following:
def try4(request):
if request.method == "POST":
form = snselect(request.POST)
if form.is_valid():
data = form.cleaned_data
# Get the selected item from your choice field and retrieve the
# corresonding model object with that id
...
I've been scanning through Django documentation, and Google search results, all afternoon and I'm still somewhat stuck in my attempt to create a dynamic form. I'm hoping I just need someone to nudge me in the right direction :-) I'm just starting to learn Django, so I'm still very much a beginner; however, I'm already an intermediate python user.
What I'm trying to do is create a dynamic form, where the user makes a selection from a drop-down menu, and based on that selection another part of the form will automatically update to display results relevant to the currently selected item, but from another database table.
I'll try and use a simplified version of the models from the Django tutorial to better illustrate what I'm trying to do:
# models.py
from django.db import models
class Poll(models.Model):
question = models.CharField(max_length=200)
class Choice(models.Model):
poll = models.ForeignKey(Poll)
choice = models.CharField(max_length=200)
So lets say I want to have something like a drop-down selection field, populated with the question from each Poll in the database. I also want to have a text-field, which displays the corresponding choices for the currently selected Poll, which will update on-the-fly whenever the user selects a different Pool. I've been able to figure this out by placing a button, and posting information back to the form; However, I'm trying to do this automatically as the user makes a selection. My view sort of looks something like this at the moment:
#view.py
from django import forms
from django.shortcuts import render_to_response
from myapp.models import Poll,Choice
class MyModelChoiceField(forms.ModelChoiceField):
def label_from_instance(self, obj):
return "%s" % obj.question
class PollSelectionForm(forms.Form):
polls = MyModelChoiceField( queryset=Poll.objects.all() )
class ChoiceResults(forms.Form):
def __init__(self, newid, *args, **kwargs):
super(ChoiceResults, self).__init__(*args, **kwargs)
self.fields['choice'] = forms.TextField( initial="" )
def main(request):
return render_to_response("myapp/index.html", {
"object": PollSelectionForm(),
"object2": ChoiceResults(),
})
My template is very simple, just something like
{{ object }}
{{ object2 }}
I'm sure the way I'm going about creating the forms is probably not the best either, so feel free to criticize that as well :-) As I mentioned, I've read solutions involving reposting the form, but I want this to happen on-the-fly... if I can repost transparently then that would be fine I guess. I've also seen libraries that will let you dynamically create forms, but that just seems like overkill.
Here is one approach - Django/jQuery Cascading Select Boxes?
You can create a new view that just renders json to a string,
and then trigger an event when you're done selecting from the first list which loads the data dynamically from that json.
I do a similar thing here, populating a form based on a selection in a drop down. Maybe this helps you.
Here is the model of the values used to pre-populate the form:
class OpmerkingenGebrek(models.Model):
opmerking = models.CharField(max_length=255)
advies = models.CharField(max_length=255)
urgentiecodering = models.CharField(max_length=2, choices=URGENTIE_CHOICES_2011)
bepaling = models.CharField(max_length=155,blank=True,null=True)
aard = models.CharField(max_length=3, choices=AARD_CHOICES)
The view that manages the form:
def manage_component(request,project_id,.....):
# get values for pre-populate
og = OpmerkingenGebrek.objects.all()
.........
formset = ComponentForm(request.POST,request.FILES)
.........
)))
return render_to_response(template, {
'formset':formset,
........
'og':og,
},context_instance=RequestContext(request))
The html the renders the form
{% extends "base.html" %}
{% block extra_js %}
<script type="text/javascript" src="/media/js/limitText.js"></script>
<script type="text/javascript" src="/media/js/getValueOpmerking.js"></script>
{% endblock %}
<form enctype="multipart/form-data" method="post" action="">
{{ formset.as_table }}
</form>
<p>Choose default values:</p>
<select id="default" onChange="getValue(this)">
{% for i in og %}
<option value="{{ i.opmerking }} | {{ i.advies }} | {{ i.urgentiecodering }} |
{{ i.aard }} | {{ i.bepaling }}">{{ i.opmerking }}</option>
{% endfor %}
</select>
The javascript that pre-populates the form:
function getValue(sel)
{
//get values
var opm = sel.options[sel.selectedIndex].value;
//split string to parts
var parts = opm.split("|");
// autofill form
var opmerking = document.getElementById("id_opmerking");
opmerking.value = parts[0];
var aanbeveling = document.getElementById("id_aanbeveling");
aanbeveling.value = parts[1];
var opt = document.getElementById("id_urgentie");
var urgentie = opt.selectedIndex;
for(var i=0;i<opt.length;i++){
if(opt.options[i].value == parts[2].split(' ').join('')){
opt.selectedIndex = i;
}};
var opt = document.getElementById("id_aard");
var aard = opt.selectedIndex;
for(var i=0;i<opt.length;i++){
if(opt.options[i].value == parts[3].split(' ').join('')){
opt.selectedIndex = i;
}};
var bepaling = document.getElementById("id_bepaling");
bepaling.value = parts[4];
};
I have declared a table and want to fetch the row's value which is checked using checkboxfield. Any help, how can i write this event in my views so that everytime I select a row and hit submit button, it returns the row's values.Code goes like this:
class mytables(tables.Table):
new_database = tables.CheckBoxColumn()
student =tables.Column(accessor='Student')
Class = tables.Column(accessor='class')
And in my templates a submit button.
You need to choose a suitable value for the CheckBoxColumn. Generally if you're displaying a queryset, you'll use the pk of each object for the CheckBoxColumn. In your case this would look like:
class EnrollmentTable(tables.Table):
selection = tables.CheckBoxColumn(accessor='pk')
student = tables.Column()
class = tables.Column()
Then you'll need to render the table within a form, so that the user can submit the form, e.g.:
<form action="/someurl/" method="post">
{% load render_tables from django_tables2 %}
{% render_table table %}
<input type="submit"/>
</form>
Then you'll need a view hooked up to /someurl/. In your case the view will need to look at the POST variable selection:
def someview(request):
if request.method == "POST":
pks = request.POST.getlist("selection")
selected_objects = SomeModel.objects.filter(pk__in=pks)
# do something with selected_objects
else:
# ...