Django form validation in template - python

I have a Django project with many standard HTML forms.
And now I want to make another form to upload documents. I found solution using django-forms. So for me it is just a simple class:
class DocumentForm(forms.Form):
docfile = forms.FileField(
label='',
)
description = forms.CharField(
label=u'Description:',
widget=forms.Textarea(attrs={'rows':4, 'cols':50}),
)
I want to add emptyness check to Textarea field. There is a possibility to add "required" parameter in CharField constructor. But in this case page will allow POST and I should write extra code after "form.is_valid()" check and use "form.description.errors" to display my error message.
And I want behaviour similar to my other forms. Something like
<textarea name="action" required rows="4" cols="50"></textarea>
In this case I can't even do POST without non-empty textarea. And more important I have error display as I like.
Is there a possibility to make such form with django-forms? Also is there a possibility to make similar behaviour to FileField?

You can add the required attribute just like you have added rows or cols:
class DocumentForm(forms.Form):
docfile = forms.FileField(
label='',
required=True, # just in case someone doesn't submit using form
widget=forms.ClearableFileInput(attrs={'required': 'required'}),
)
description = forms.CharField(
label=u'Description:',
required=True, # just in case someone doesn't submit using form
widget=forms.Textarea(attrs={'rows': 4, 'cols': 50, 'required': 'required'}),
)
Please note the use of forms.ClearableFileInput widget for FileField because that is what's defined as the default widget for FileField.

Related

Django How to add a model other than a form

I am making an app using Django2.0. But There are things that I do not know even if I investigate.
There are models as follows.
class Picture(models.Model):
place = models.ForeignKey(Place, on_delete=models.CASCADE)
title = models.CharField(max_length=255)
image = models.ImageField(upload_to='image/')
using the following form
class PictureForm(forms.ModelForm):
class Meta:
model = Picture
fields = ['place', 'title', 'image']
And run following code on views.py
obj = Picture()
pic = PictureForm(request.POST, request.FILES, instance=obj)
pic.save()
I can add model peacefully. But in this way, I should select place and title on form every time. Beforehand I decide place and title, I will only select image on the form. It reduces my work.
Since there are several place and title, I do not want to set them to default values.
I succeeded in making a form.
class PictureForm(forms.ModelForm):
class Meta:
model = Picture
fields = ['image',]
But I don't know how to add title and place on views.py
I'm not good at English. Thank you for seeing the bad sentensecs to the end
Your issue comes from the view code, where you instantiate your new model.
If you want to create a new model you don't need to pass the insance argument, just edit the lines to get the following :
pic = PictureForm(request.POST, request.FILES)
pic.save()
See https://docs.djangoproject.com/en/2.0/topics/forms/modelforms/#the-save-method
To be able to set only a few fields you should update your model declaration, with for example null=True, Blank=True, default=None in the optional fields arguments.
PS : the django documentation is transalated in many languages, but if you don't find yours try the IRC channels.
If I understand your problem clearly,
You have predecided the place and the title and want the user to enter only the image. Only image should be kept in the form, but you want to enter the title and the place in the view.If that's the case,do this :
Your view function:
def some_view(request):
if request.method == "POST":
T,P = get_title_and_place()
# This fuction will return a tuple with the title and the place that you need.
# Or Alternatively, you can pass the title and place as argument to the view
obj = Picture(title=T,place=P)
#Initialise object with title and place received from function or arguments
pic_form = PictureForm(request.POST, request.FILES)
#The form contains only the image field as in your last block of code
if pic_form.is_valid():
obj.image = request.FILES['image']
obj.save()
#Save the object
return redirect('some_url')
else:
return render('xyz.html',{"form":pic_form})
#Incoming image is invalid. Throw back the invalid form to the user
else:
#Incoming method is not POST, give user the form to fill the image
pic_form = PictureForm()
return render('xyz.html',{"form:pic_form})
On submission of the form from xyz.html, you should redirect back to this view. This is the reason why I made a seperate function to get the title and place so that user doesn't know about it via the url.
However be careful with this method. You have to take care of validating the title and place. Otherwise the obj.save() will throw error

Django form in a base class

forms.py
class search_form(forms.Form):
options = categories.objects.all()
category = forms.ModelChoiceField(options, initial={'All':'All'}, label='')
search = forms.CharField(max_length=100, label='', required=False)
This form is used for searching for items. And right now I have it implemented on the index page and it works as expected. The index (home) page has its own view that makes use of this form, but I have a base template (base.html) that every page on the site extends. The base template holds the menu bar and the footer of the site. I need to add the form to the base class and have it function in every template that extends it.
Is there a way to do this?
You can add a custom context processor, which is useful for passing data to every template context, which will give every template access to your form.
As explained in the docs, you need to make a function that will return a dictionary containing your form, and point to it in the settings.

Suit form include on list display in admin

Im trying to use Django Suit's form includes to add a button to the list display of all my subscribers in the admin. In the documentation it says to add this to your admin.py file in the right app.
class SubscriberAdmin(admin.ModelAdmin):
list_display = ('email', 'date')
readonly_fields = ('email', 'date')
def has_add_permission(self, request):
return False
suit_form_includes = (
('admin/suit_includes/suit_csv.html', 'top'),
)
However this only appears when clicking into an instance of an object, and doesn't show up on the admin page that shows the entire list of objects in the database. Is there a way to do this with Django Suit ? I had trouble finding anything on google.
suit form include template:
<button class="btn btn-info">Export to File</button>
Admin instance display (Where its appearing):
Admin list display (Where I want it to appear):
What's doing django-suit, here, is that it is including your HTML snippet in the change_form that is displayed for your model, the change_form being where you can modify your model and save the changes into the database.
Where you want it to appear is the "change_list", aka the place where you can see all of your instances of that model in your database.
To add it your html snippet, you should extend your change_list.html with your own snippet : More info on expanding templates in the official documentation
Good luck !

Django autocomplete_light and cities_light - invalid choice

I have a model with a location field that is mapped to cities_light.city and I'm using an autocomplete field that allows users to type in their city and have it autocompleted to the correct/valid location model instance.
class Profile(models.Model):
location = models.ForeignKey(City, blank=True, null=True)
class ProfileForm(ModelForm):
class Meta:
model = Profile
fields = ('location')
widgets = {
'location': autocomplete_light.TextWidget(CityAutocomplete, autocomplete_js_attributes={'placeholder':'City, Country', 'minimum_characters':4})
}
The form field works exactly as advertised and a list of autocomplete options are shown. However when I save the form/model I get a validation error which seems to be caused by the field not being translated into the primary key of the City model instance.
Select a valid choice. That choice is not one of the available choices.
I'm guessing I need to extend the AutocompleteModelBase like the CityAutocomplete implemented below but I'm not sure how and I've been unable to find a working example.
class CityAutocomplete(autocomplete_light.AutocompleteModelBase):
search_fields = ('search_names',)
https://github.com/yourlabs/django-cities-light/blob/master/cities_light/contrib/autocompletes.py
Thank for any assistance and I'm sorry if my question is poorly formatted.
Your problem is not specific to django-autocomplete-light. What you're doing has no chance to work and here's why:
the form field for a ForeignKey like location is a ModelChoiceField by default,
ModelChoiceField accepts values which are pks of models in ModelChoiceField.queryset, which is TheModel.objects.all() by default,
the TextWidget widget is a TextInput,
a TextInput widget is just an <input type="text" />,
the value of a <input type="text" /> is directly sent to the server on form submission.
As a result, selecting a couple of cities like "Lisboa" and "Madrid" with a text widget will look like::
<input type="text" value="Lisboa, Madrid" name="location" />
Which means that the form will post {'location': 'Lisboa, Madrid'}. While this is good for a CharField, it won't work for a ModelMultipleChoiceField which would expect something like {'location': [3,5]} where 3 would be the pk of Lisboa and 5 the pk of Madrid.
In the same fashion, a ModelChoiceField would expect {'location': 3} which autocomplete_light.ChoiceWidget is able to do.
To fix this, use a ChoiceWidget instead of a TextWidget. I have clarified this in the tutorial I hope it is better now.

Django add single fields to Model form using AJAX call

I have a Model form. The form contains a button "Add more Field". Clicking this button sends an AJAX call which should add a textfield to the form. Any number of "textfields" can be added.
I am not sure if Django-Formsets is the correct way to do it as I need to store the data of "extra added fields" in the same Model Form's Table in the database.
How can I achieve this ?
I did something similar to this recently, and my solution was to subclass the form dynamically, providing only the field needed, and rendering just that field:
from forms import SomeModelForm
from models import SomeModel
def view_name(request,pk,field):
SomeModelFieldForm(SomeModelForm):
class Meta(SomeModelForm.Meta):
fields = (field,)
inst = SomeModel.objects.get(pk=pk)
form = SomeModelFieldForm(instance=inst)
#the rest of your view goes here...
#...send only the form's field:
ctx = {'field': form[field]}
return render_to_response("template",ctx)
This takes advantage of your original form's specifics - i.e., if you have specially defined widgets, or other restrictions, or something. It then restricts the entire form to a single field. This is to allow the validation of a single field on an existing model.
However, you don't want to send the entire form, you only want to send the single field. form[field] is a django.forms.forms.BoundField that represents the specific field you pass in.
For example, if you were working with the User model from django.contrib.auth.models, and you created a form for a specifi user, in idle calling form["username"] would return:
<django.forms.forms.BoundField object at 0x01354750>
and calling print form["username"] would print:
<input id="id_username" type="text" name="username" value="name_of_user" maxlength="30" />

Categories