I am just starting to work with Django and I have some problems with forms and dropping lists.
I have a model with two attributes, and I want to display one of the attributes in a dropping down list (this one will be unchangeable) and another one in a text field (this one will be changeable). Also, I have a submit button, so I want to change a second attribute in a text field and by pressing on the button. How can I do this? What would some examples be?
As you are starting to work with Django, you might or might not know about how Django handle forms.
In Django, forms can be handled in two ways:
User-created and managed forms (without any form class)
Class-managed forms (connected to Django models)
Documentation form Django Forms
Now let’s talk about the first type of forms (where you create your HTML form and manage the request sent to server):
These forms are simple to make and when there are only a few and are only suggested when you have a very small amount of inputs (I would say four or fewer inputs).
Here is a simple example of subscription of a newsletter with an email example.
<form id='sub-form' method="POST">
{% csrf_token %}
<div>
<input type="email" name="sub_email">
</div>
<input class="button" value="Subscribe" type="submit" id="subbutton">
</form>
So a very important thing to look at here is {% csrf_token %}, about which you can read more about here and about how it works and prevents cross-site request forgery. This token will be required when you make a request to Django server with any post request and data.
In this subscription form you see one <input> with name="sub_email". Take note of this as we will use this to get this value on the server as this is the key to its value, and then a simple Submit Button.
When you press Submit on a page let’s say url = "http://BASE_URL/home" you will receive a POST request on the view that handles that URL.
So now coming to the view.py, let’s say you only allow registered users to subscribe then the view will go something like this (assuming you are not expecting any other request from the home URL).
def home(request):
user=request.user
if request.method == "POST":
if user.is_authenticated:
email = request.POST['sub_email'] #Using name of input
#Logic to save this email
return HttpResponse("You are Subscribed",status=200)
else:
return HttpReposnse("You are not Authenticated",status=401)
else:
return render(request,"home.html")
Now as you are the expert of simple forms, let’s work with Django class-based forms.
These views are a little work when you have very few inputs, but they are a great help in manageability and when you have to work with large number of inputs.
You will request these Class Based Forms as in your question you are trying to send an instance of a model from your Models.py to a form to user.
I have a model of Posts that can be used for this example:
class Post(models.Model):
postTitle = models.CharField(max_length = 90,null=True)
subTitle = models.CharField(max_length = 160,null=True)
location = models.CharField(max_length = 3,default = 'IN',null=True)
Now according to your question, you are trying to let the user change one attribute, let’s say postTitle and for location you are not letting the user select one of the countries which is preselected and for your post.
Now we have to create a form for this. Forms in class based are created in Forms.py. If you don't have forms.py then you can create one right along models.py and views.py.
Now for the form, I would like to edit some existing data as you are saying one of the attributes (Fields) is fixed and another editable, but you get the value from the model.
class PostEditForm(ModelForm):
location = forms.CharField(label='Country ',widget=forms.Select(attrs={'class': 'Classes_HERE','placeholder':' Select a Country','disabled':'disabled'} ,choices=country_list),required=True)
class Meta:
model = Post
fields= ['postTitle','subTitle','location']
labels = {
'postTitle':'Title',
'subTitle':'Sub-Title',
}
widgets = {
'postTitle': forms.TextInput(attrs={'class': 'mention_class_here','placeholder':' Add Title'}),
'subTitle': forms.TextInput(attrs={'class': 'mention_class_here','placeholder':' Add Sub-Title'})
}
Attributes can be mentioned in forms fields the way I have mentioned them in the above example. I used disabled="disabled" to disable (not editable) location field and used forms.Select to make it drop down.
You might also see that I gave the location field a list to choose from. This is how you can create a list of your items. It's been quite some time when I wrote this, so there might be errors or it may not work for you, so just make sure you are referring to the current documentation and searching Stack Overflow for answers.
country_list = [
('', 'Select a Country'),
("AF", "Afghanistan"),
("AX", "Aland Islands"),
("AL", "Albania"),
("DZ", "Algeria"),
("AS", "American Samoa"),
("AD", "Andorra"),
("AO", "Angola"),
("AI", "Anguilla"),
("AQ", "Antarctica"),
("AG", "Antigua And Barbuda"),
("AR", "Argentina"),
("AM", "Armenia"),
("AW", "Aruba"),
.
.
.
Now this form can be passed as context in a view to an HTML page.
def editPost(request,post_id):
user=request.user
post = get_object_or_404(Post,id=post_id) #Getting the instance of Post
if user.is_authenticated:
formPost = PostEditForm(request.POST or None,instance=post)
if request.method=='POST':
if formPost.is_valid():
savedPost=formPost.save()
else:
return render(request,'postEdit.html',{'formPost':formPost})
else:
return HttpResponse("Not Authorized",status:401)
Now your HTML file postEdit.html should look something like this:
<form id="post-form" method="POST" enctype="multipart/form-data">
{% csrf_token %}
<div>
{{formPost}}
</div>
</form>
That is it and adding a submit button in the same form, you can now edit your instance of post that you passed along with this {{formPost}}. Combine your logic wherever you think needs a change to fit in what you want to do.
By no means I am saying all this code is in working condition, but it is shown only to illustrate the flow and working.
I have a Django template that renders a table comprising Django formsets.
Template (simplified)
<table id = 'my_table'>
{% for form in my_formset %}
<tr><td>{{form.my_field}}</td></tr>
{% endfor %}
</table>
Now, I have a Jquery code which looks something like this:
$(document).on('change', '#my_table tr input[type = text], input[type = number]', function(){
// currently the following code handles save event
// $.post ('/save_my_form/', $form.serialize())
});
And the corresponding Django view
def save_my_form(request):
# .......
for form in order_draft_formset:
if form.is_valid():
form.save()
# .......
The problem with this approach is that I modify only one single input element in one single form, whereas the Django view loops through entire formset. The question is, is there any built-in Django way to fetch the form within which the modified input is localized, so that I save only this exact form in my view without going through the entire formset. I feel that Jquery must somehow submit some info via post-request parameters that would help Django do that. As for now, I am thinking of parsing the automatically Django-generated id, f.e, id_form-5-my_field, and getting "5" out of it in Jquery and passing this "5" to Django. But I have a terrible feeling that this is a "dirty" method, and there must be a cleaner way to do that. Any ideas on this ?
if form.has_changed():
if form.is_valid():
form.save()
I want to add a form to the list display of my ModelAdmin, but can't get the csrf_token to render properly. I'm using django 1.6. My code looks like this:
class ApplicationAdmin(admin.ModelAdmin):
model = Application
list_display = ('applicant', 'approve_or_reject')
def approve_or_reject(self, obj):
return '<form method="post" action="/applications/approvals">{% csrf_token %}<input type="submit" class="btn-approve" name="approve" value="Approve"/></form>'
approve_or_reject.short_description = 'Approve/Reject'
approve_or_reject.allow_tags = True
admin.site.register(Application, ApplicationAdmin)
I keep getting the error:
KeyError at /management/application/ '% csrf_token %'
How can I properly pass the csrf_token?
Model admin methods used in list_display like approve_or_reject should return text. If you mark the output as safe, you can return HTML. However, the return value is not treated like Django template language, so using the csrf token tag won't work.
It wouldn't be easy to get the csrf token inside the approve_or_reject method, because you do not have access to the request object. Another issue is that the entire changelist table is already wrapped in a form tag (id="changelist-form"), and form tags should not be nested.
An alternative would be to implement your 'approve or reject' functionality as an admin action. The UI would be different, but it might be good enough.
Duplicate question to Django-Taggit in Edit Form. However, the answer doesn't work for me.
I'm using Django Taggit in a form. The form is populated by an instance of an object that has Django Taggit enabled.
In my template, the forms tag input field value is set like so:
value="{{ form.tags.value|default_if_none:"" }}"
This results in a string value in the rough format of:
value="[<TaggedItem: foo tagged with bar>]"
If I render the form using basic Django form rendering ( i.e. {{form}} ), the tag field value is rendered correctly ( i.e. "tag1, tag2" ). Strangely, this is the opposite to what the poster of Django-Taggit in Edit Form was experiencing. For them, {{ form }} wasn't rendering the value correctly, but for me, it is.
Why is there this difference between my form and Django's? How can I make the tag value render correctly in my custom form template?
I have a solution that feels hacky but works.
When instantiating the form, modify the initial state for the tags:
form = YourModelForm(request.POST or None, instance=your_model_instance, initial={
'tags' : edit_string_for_tags(your_model_instance.tags.get_query_set())
})
edit_string_for_tags() and get_query_set() are part of Django Taggit.
Note: You'll need to import edit_string_for_tags(). i.e. from taggit.utils import edit_string_for_tags
I'm building a website using Flask, and on one page I've got two forms. If there's a POST, I need to decide which form is being posted. I can of course deduct it from the fields that are present in request.form, but I would rather make it explicit by getting the name (defined by <form name="my_form">) of the form that is submitted. I tried several things, such as:
#app.route('/myforms', methods=['GET', 'POST'])
def myForms():
if request.method == 'POST':
print request.form.name
print request.form.['name']
but unfortunately, nothing works. Does anybody know where I can get the name of the form submitted? All tips are welcome!
There is no 'name of the form'. That information is not sent by the browser; the name attribute on <form> tags is meant to be used solely on the browser side (and deprecated to boot, use id instead).
You could add that information by using a hidden field, but the most common way to distinguish between forms posting to the same form handler is to give the submit button a name:
<submit name="form1" value="Submit!"/>
and
if 'form1' in request.form:
but you could also use a <input type="hidden"> field to include the means to distinguish between forms.