I am trying to validate a User Profiling form in django and I can't. It seems that there is something wrong with forms.dateField(). It does not validate (ie. is_valid() return false)
this is my forms dateField entry:
date_of_birth = forms.DateField(label=u'date of birth', input_formats='%d/%m/%Y', required=False, widget=forms.DateInput(format = '%d/%m/%Y'))
I noticed that request.POST.get('date_of_birth', '') returns the correct date (ie. the date I have typed in the html form field).
I also noticed that in this function:
def clean_date_of_birth(self):
date = self.cleaned_data['date_of_birth']
date object is always None.
What am I doing wrong?
EDIT:
This is what I am trying to enter:
29/07/1974 (July 29th, 1974)
This is the output of 'submit' (various requests)
29/07/1974
profile form is *NOT* valid
[23/Feb/2012 12:16:27] "POST /profile/chris/ HTTP/1.1" 200 16289
29/7/1974
profile form is *NOT* valid
[23/Feb/2012 12:16:33] "POST /profile/chris/ HTTP/1.1" 200 16289
1974-07-29
profile form is *NOT* valid
[23/Feb/2012 12:18:15] "POST /profile/chris/ HTTP/1.1" 200 16289
This is my template
<div class="input_area">
<form id="profile_form" method="post" action="/profile/{{ user.username }}/">{% csrf_token %}
{{ form.as_p }}
<input type="submit" id="submit" value="save" class="submitButton idle" style="width:70px" />
</form>
</div>
this is my views.py
def profile(request, username):
form = ProfileForm(request.POST)
print request.POST.get('date_of_birth', 'None')
try:
user = User.objects.get(username=username)
except User.DoesNotExist:
raise Http404(u'User not Found')
if form.is_valid():
print 'profile form is valid'
else:
print 'profile form is *NOT* valid'
and finally this is my forms.py (do not use clean_data functions at the moment)
class ProfileForm(forms.Form):
tz = []
timezones = Timezone.objects.all()
for timezone in timezones:
val = str(timezone.hour)
v = val.split(':')
tuple = (timezone.id, '('+timezone.sign+''+v[0]+':'+v[1]+') '+timezone.name)
tz.append(tuple)
sex = [('male','male'),('female', 'female'),('unknown', 'prefer not to say')]
real_name = forms.CharField(label=u'real name', widget=forms.TextInput, required=False)
date_of_birth = forms.DateField(label=u'date of birth', input_formats='%d/%m/%Y', required=False, widget=forms.DateInput(format = '%d/%m/%Y'))
pp_email = forms.EmailField(label=u'Paypal Email', widget=forms.TextInput, required=False)
gender = forms.ChoiceField(label=u'sex', choices=sex, widget=forms.Select(), required=False)
timezone = forms.ChoiceField(label=u'time zone', choices=tz, widget=forms.Select())
address = forms.CharField(label=u'street address', widget=forms.Textarea, required=False)
postal = forms.CharField(label=u'postal code', widget=forms.TextInput, required=False)
input formats in DateField must be list or tuple https://docs.djangoproject.com/en/dev/ref/forms/fields/#django.forms.DateField.input_formats
With Django 1.6 and up you can use the localized_fields in your form's Meta or localize=True in your form. See https://docs.djangoproject.com/en/1.9/topics/i18n/formatting/#format-localization.
When using USE_L10N = True, Django will use the formats.py file for your locale (part of LANGUAGE_CODE).
You can end up with something DRY like this (as the fields specified in models.py do not need to be repeated in forms.py):
class SomeForm(forms.Form):
class Meta:
model = SomeModel
fields = ('first_name', 'dob',)
localized_fields = ('dob',)
Related
Account is my AUTH_USER_MODEL and AccountDisplayInfo consists of all the additional display info of every account. So they can input and submit, and subsequently update their information. These are my codes, but I'm unsure why it isn't working. First of all, I am receiving this error:
DoesNotExist at /account/5/displayinfo/ AccountDisplayInfo matching query does not exist.
Secondly, the "update" function isn't working.
models.py
class Account(AbstractBaseUser):
email = models.EmailField(verbose_name="email", max_length=60, unique=True)
username = models.CharField(max_length=30, unique=True)
class AccountDisplayInfo(models.Model):
account = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
instagram = models.CharField(max_length=50, unique=True, blank=True, null=True) #instagram
.html
<form method="POST" enctype="multipart/form-data">{% csrf_token %}
{{ form.as_p }}
<div class="d-flex justify-content-center">
<button type="submit" class="btn btn-primary btn-sm col-lg-5">Update</button>
</div>
</form>
views.py
def display_information_view(request, *args, **kwargs):
user_id = kwargs.get("user_id")
account = Account.objects.get(pk=user_id)
context = {}
displayinfo = AccountDisplayInfo.objects.get(account=account)
if request.POST:
form = DisplayInformationForm(request.POST, request.FILES, instance=request.user)
if form.is_valid():
info = form.save(commit=False)
info.account = request.user
info.save()
messages.success(request, 'Your profile display information have been updated', extra_tags='editdisplayinfo')
return redirect("account:view", user_id=account.pk)
else:
form = DisplayInformationForm(request.POST, instance=request.user,
initial={
"instagram": displayinfo.instagram,
}
)
context['form'] = form
else:
form = DisplayInformationForm(
initial={
"instagram": displayinfo.instagram,
}
)
context['form'] = form
return render(request, "account/displayinfo.html", context)
forms.py
class DisplayInformationForm(forms.ModelForm):
class Meta:
model = AccountDisplayInfo
fields = ('instagram')
Also, would be great if you can advise on this::
If I have 2 tables. Table 1 and Table 2. Table 2 has a foreign key to table 1 but table 1 dont have a foreign key to table 2. How can I query table 2's data from table 1? Thanks
By default .get() will return a DoesNotExist exception if no object matches the query you executed and stop the code from running, so if you want to input it manually on the same page use filter instead:
displayinfo = AccountDisplayInfo.objects.filter(account=account).first()
Then in your template do something like this:
{% if displayinfo %}
... show display info...
{% else %}
<p> No info yet </p> <!-- (or show some form) -->
{% endif %}
To answer your other question:
You have to use the related_name or related models attribute to access the ForeignKey data or use the model name with the _set suffix, for example:
class Post(models.Model):
title = models.CharField(max_lenght=10)
class Comment(models.Model):
body = models.CharField(max_lenght=200)
post = models.ForeignKey(Post, on_delete=models.CASCADE, related_name='comments')
then you would get the Post and its comments:
post = Post.objects.get(pk=1)
comments = post.comments.all()
if you didn't have the related_name attribute in your model field you would do this instead:
comments = post.comment_set.all()
UPDATE
Maybe the issue is in your Form class, try removing the save method from it and instead do this in your view:
if request.POST:
form = DisplayInformationForm(request.POST, request.FILES, instance=request.user)
if form.is_valid():
info = form.save(commit=False)
info.account = request.user
messages.success(request, 'Your profile display information have been updated', extra_tags='editdisplayinfo')
info.save()
return redirect("account:view", user_id=account.pk)
new in the field and struggling with a form validation in practice. I have created a form which i use as data input for a search in the DB. Form validation is only being triggered for the first field while the others seems not to be taken in consideration, bellow the code:
Form description:
class SearchForm(forms.Form):
cnp_nbr = forms.IntegerField(label='CNP', widget=forms.TextInput(attrs={'class': 'form-control' }), required=False)
first_name = forms.CharField(label='First Name', widget=forms.TextInput(attrs={'class': 'form-control'}), required=False)
last_name = forms.CharField(label='Last Name', widget=forms.TextInput(attrs={'class': 'form-control'}), required=False)
class Meta():
model = Clients
fields = ('cnp_nbr','first_name','last_name')
def clean(self): # most used!!!
all_clean_data = super().clean()
cnp_nbr = all_clean_data['cnp_nbr']
first_name = all_clean_data['first_name']
last_name = all_clean_data['last_name']
if cnp_nbr is None or (first_name is None and last_name is None):
raise forms.ValidationError("Enter f1 or f2&f3")
super().clean()
Views:
class ClientsSearch(generic.FormView):
form_class = forms.SearchForm
template_name = "administration/clients_search.html"
success_url = reverse_lazy('administration:searchresults')
def form_valid(self, form):
self.request.session['cnp_nbr'] = form.cleaned_data['cnp_nbr']
self.request.session['first_name'] = form.cleaned_data['first_name']
self.request.session['last_name'] = form.cleaned_data['last_name']
return super().form_valid(form)
class SearchResults (generic.ListView):
model = models.Clients
template_name='administration/search_results.html'
context_object_name = 'all_search_results'
def get_queryset(self):
return self.model.objects.filter(
Q(cnp_nbr__exact=self.request.session['cnp_nbr']) | Q(first_name__exact=self.request.session['first_name']) & Q(last_name__exact=self.request.session['last_name'])
)
HTML for search form:
<form method="POST" >
{% csrf_token %}
{{ form.as_p }}
<button class="btn btn-primary" id="search_submit" name = "search_submit" type="submit" > Search</button>
</form>
Validation is working only for cnp_nbr, i even tested them one by one.
This works :
if cnp_nbr is None:
raise forms.ValidationError("Enter field")
This doesnt
if first_name is None:
raise forms.ValidationError("Enter field")
Search is working just fine! Thanks in advance
That's because a not required IntegerField will be None if it's empty, whereas a not required CharField will be '' (the empty string) if it's empty. So you should not check last_name is None but just not last_name:
if cnp_nbr is None or not (first_name or last_name):
Note: Also I'd advise you to never directly assume the keys are present in the form's cleaned_data: Do first_name = all_clean_data.get('first_name') instead.
Note2: Why don't you just make cnp_nbr required since your ValidationError will trigger in any case if cnp_nbr isn't filled?
I have a user profile model I use django model forms to create and edit the users profiles Now I want to change only 2 fields on the profiles lat and lon . So on my Index.html I have a small html form . As soon as the user clicks locate me. The latitude and longitude are automatically filed in and the submit button will be clicked using Jquery. How do I use the details from this form to update my users lat and lon. Its just that I have not used django's HTML form's and I need to update the lat lon entered on this mini form to the users profile
<form method="post" action="{{ ??????????? }}">
<input id="jsLat" type="text" placeholder="latittude" >
<input id="jsLon" type="text" placeholder="longitude">
<button type="submit" id="submit">Submit</button>
</form>
Do I create another view & url (that way I will have 2 profile edit views and 2 profile edit url's) to add the lat and lon to my existing profile. Or Is there a way I can use the existing view and url and update the 2 fields
below are my models
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
city = models.CharField(max_length=100)
age = models.DateField(blank=True, null=True)
profile_image = models.ImageField(default='', blank=True, null=True)
null=True)
is_verified = models.BooleanField(default=False)
lat = models.FloatField(blank=True, null=True)
lon = models.FloatField(blank=True, null=True)
Below is my profiles view
#login_required
def edit_profile(request):
if request.method == 'POST':
user_form = UserEditForm(data=request.POST or None, instance=request.user)
profile_form = ProfileEditForm(data=request.POST or None, instance=request.user.profile, files=request.FILES)
if user_form.is_valid() and profile_form.is_valid():
user_form.save()
profile_form.save()
return redirect('accounts:profile', username=request.user.username)
else:
user_form = UserEditForm(instance=request.user)
profile_form = ProfileEditForm(instance=request.user.profile)
context = {'user_form': user_form,
'profile_form': profile_form}
return render(request, 'accounts/profile_edit.html', context)
Below are my forms.py
class UserEditForm (forms.ModelForm):
class Meta:
model = User
fields = ('first_name', 'last_name', 'email', 'username')
class ProfileEditForm(forms.ModelForm): #UserProfileForm or ProfileEdit
class Meta:
model = Profile
fields = ('city', 'age', 'profile_image','lat','lon')
Below are my urls
urlpatterns = [
url(r'^profile/(?P<username>[-\w]+)/$', views.ProfileView.as_view(), name='profile'),
url(r'^edit_profile/$', views.edit_profile, name='edit_profile'),
#Profile created automatically when user is created
]
PS: I have trimmed the geodjango code from here as it's not a part of this question
You definitely can edit the details within the post of your view.
Firstly, in your HTML you should name your inputs.
<form method="post”>
<input id="jsLat" type="text" placeholder="latittude" name=“Lat” >
<input id="jsLon" type="text" placeholder="longitude" name=“Long”>
<button type="submit" id="submit">Submit</button>
</form>
Then in your post view you can retrieve the data by doing: lat = request.POST[‘Lat’] or lon = request.POST[‘Lon’]
Next step is to update the database entry.
To do this you need to retrieve the user’s object.
user = User.objects.get(instance=request.user)
Once you have this you can update fields in the following manner:
user.latitude = lat
user.longitude = lon
user.save()
Further reference:
1. Retrieving data from HTML form
2. Updating database entry
Hope this helps!
Edit:
It is rather recommended that you have the form as a Django form and then simply add the desired HTML attribute there. This allows you to call the form as you usually would for a Django form.
I had to create a form from which some details go to default.auth.user model and some to my custom model so after searching from various sources I did this:
Django Version :1.7
model.py
class UserProfile(models.Model):
user = models.OneToOneField(User)
title_id = models.ForeignKey('Title')
mobile_number = models.CharField(max_length=10)
alternate_number = models.CharField(max_length=10)
date_of_birth = models.DateField()
profession_id = models.ForeignKey('Profession', null=True, blank=True)
house_no = models.CharField(max_length=100, blank=True, default='NA')
city_id = models.ForeignKey('City', null=True)
country_id = models.ForeignKey('Country', null=True)
state_id = models.ForeignKey('State', null=True)
locality_id = models.ForeignKey('Locality', null=True)
profile_picture_path = models.CharField(max_length=100, blank=True, default='NA')
forms.py:
class UserForm(forms.ModelForm):
password = forms.CharField(widget=forms.PasswordInput(attrs={'id': 'password'}))
email = forms.CharField(widget=forms.TextInput(attrs={'id': 'email_id'}))
username = forms.CharField(widget=forms.TextInput(attrs={'id': 'username'}))
first_name = forms.CharField(widget=forms.TextInput(attrs={'id': 'first_name'}))
last_name = forms.CharField(widget=forms.TextInput(attrs={'id': 'last_name'}))
class Meta:
model = User
fields = ('email', 'username', 'first_name', 'last_name', 'password')
class ExtraDetailsForm(UserForm):
confirm_password = forms.CharField(widget=forms.PasswordInput(attrs=
{'id':'confirm_password'}),max_length=32,
required=True,)
class Meta:
model = UserProfile
fields = ('email', 'username', 'title_id', 'first_name', 'last_name',
'password', 'confirm_password',
'date_of_birth', 'mobile_number', )
My view.py is :
def register(request):
# A boolean vakue for telling whether the registration was successful
registered = False
if request.method == 'POST':
user_form = UserForm(data=request.POST)
additional_details_form = ExtraDetailsForm(data=request.POST)
if user_form.is_valid() and additional_details_form.is_valid():
user = user_form.save()
user.set_password(user.password)
user.save()
additional_details = additional_details_form.save(commit=False)
additional_details.user = user
additional_details.save()
registered = True
else:
print(user_form.errors, additional_details_form.errors)
else:
user_form = UserForm
additional_details_form = ExtraDetailsForm
return render(request,
'users/register.html',
{'user_form' : user_form, 'additional_details_form': additional_details_form, 'registerered': registered})
regsiter.html:
{% if registerered %}
<p>Thank you for register. check ur email , entered email was</p>
{% else %}
<form action="/users/register/" method="post">{% csrf_token %}
{{ additional_details_form.as_p }}
<input type="submit" value="Register" />
</form>
{% endif %}
Now the good thing is that everything is working fine and details are being stored as they should be.
But the bad thing is I do not know whether it is a correct approach or not as I did not find any tutorial/resource where this approach is used?
This is correct approach and you do it almost right. Just couple notes:
if user_form.is_valid() and additional_details_form.is_valid():
In this line if user_form is invalid then validation for additional_details_form will not run. To always validate both change it to:
if all([user_form.is_valid(), additional_details_form.is_valid()]):
In else statement you set form class to *_form variables. It should be form instances instead:
user_form = UserForm()
additional_details_form = ExtraDetailsForm()
And it may be a good idea to wrap your save code into one transaction :-)
I would recommend that you use just one form here that contains all fields.
There is no benefit to using two forms, especially since one inherits the other, this is odd behaviour when you are then passing the POST data into each of them.
Consolidate the fields into a single form and then override the 'clean' method of the form to be able to check that the two password fields match.
You can create a single form to save data into one or many different models and this is especially useful in your case since you need to validate the data for these different models together.
Ok, firstly ExtraDetailsForm shouldn't inherit from UserForm because they are for different models. It should look something like this instead:
class UserForm(forms.ModelForm):
confirm_password = forms.CharField(widget=forms.PasswordInput(attrs=
{'id':'confirm_password'}),max_length=32,
required=True,)
class Meta:
model = User
fields = ('email', 'username', 'first_name', 'last_name', 'password',
'confirm_password')
class ExtraDetailsForm(forms.ModelForm):
class Meta:
model = UserProfile
fields = ('title_id', 'date_of_birth', 'mobile_number')
Then in your view:
from django.contrib.auth import login
from django.shortcuts import redirect, render
def register(request):
user_form = UserForm(data=request.POST or None)
profile_form = ExtraDetailsForm(data=request.POST or None)
if all([user_form.is_valid(), profile_form.is_valid()]):
user = user_form.save(commit=False)
user.set_password(user.password)
user.save()
profile = profile_form.save()
# probably at this point you want to login the new user:
login(request, user)
# it's good practice to do a redirect here, after a successful
# form post, eg to display success page, as this will
# prevent accidental re-posting data if user reloads the page
return redirect('registration_success')
else:
print(user_form.errors, profile_form.errors)
return render(
request,
'users/register.html',
{
'user_form' : user_form,
'profile_form' : profile_form,
}
)
def registration_success(request):
return render('registration_success.html')
Finally you need to output both forms in the template:
<form action="/users/register/" method="post">{% csrf_token %}
{{ user_form.as_p }}
{{ profile_form.as_p }}
<input type="submit" value="Register" />
</form>
and a new template registration_success.html:
<p>Thank you for registering. Check your email, entered email was: {{ request.user.email }}</p>
I'm trying to create a basic user registration system for clients of a web application.
I've created the appropriate views and templates to create a form page which creates a Django User object and a UserProfile object of my own creation. (These are linked via a 1-1 field).
After visiting and filing in the forms on my registration page, I click submit and the fields related to initializing the UserProfile fields will be cleared and a "This field is required." error will be displayed over each input box (despite being properly filled in previously). If I fill these selected fields in again, and press submit the registration request will be processed correctly.
In the terminal, I've printed out the value of is_valid() for each form. On the first pass, the User form returns true, while the UserProfile form returns false. On the second submission they both return true.
Could you help me understand why this second form is returning false on the first pass and forcing me to resubmit?
Code is below:
models.py
from django.db import models
from django.contrib.auth.models import User
from django.forms import ModelForm
class UserProfile(models.Model):
user = models.OneToOneField(User)
name = models.CharField(max_length=100)
phone = models.CharField(max_length=32)
email = models.CharField(max_length=100)
institute = models.CharField(max_length=100)
address1 = models.CharField(max_length=100)
address2 = models.CharField(max_length=100)
city = models.CharField(max_length=100)
country = models.CharField(max_length=100)
postal_code = models.CharField(max_length=24)
description = models.TextField(max_length=2500)
def __unicode__(self):
return self.name
class UserForm(ModelForm):
class Meta:
model = User
fields = ['username', 'password', 'email']
class UserProfileForm(ModelForm):
class Meta:
model = UserProfile
exclude = ['user']
views.py
def registration(request):
if request.method == 'POST':
print('post')
user_form = UserForm(request.POST, prefix='user')
profile_form = UserProfileForm(request.POST, prefix='userprofile')
print('user form ' + str(user_form.is_valid()))
print('profile form ' + str(profile_form.is_valid()))
if user_form.is_valid() and profile_form.is_valid():
print('both valid')
user = user_form.save(commit=False)
user.is_active = False
user.save()
userprofile = profile_form.save(commit=False)
userprofile.user = user
userprofile.save()
print('success')
return HttpResponseRedirect('registration-success/')
else:
print('unbound')
user_form = UserForm(prefix='user')
profile_form = UserProfileForm(prefix='profile')
context = { 'userform': user_form,
'userprofileform': profile_form,}
return render(request, 'registration/register.html', context)
def success(request):
return render(request, 'registration/success.html', )
template.html
<!DOCTYPE html>
<html>
<body>
<h2> Registration </h2>
<form method="POST">
{% csrf_token %}
{{userform}}
</br></br>
{{userprofileform}}
<input type="submit" value="Submit"/>
</form>
forgot username/password<br />
new user
</body>
</html>
In your POST codepath, you have this:
profile_form = UserProfileForm(request.POST, prefix='userprofile')
In your else codepath, this:
profile_form = UserProfileForm(prefix='profile')
The prefix values need to match so that the POST data will be bound correctly to the profile form. It works on your resubmission because that goes through the POST codepath, so the ids used in the template match those the form object expects.