I want to show a validation message like "This email is already in use" inside my html form.
But I think i'm missing something. I keep getting an IntegrityError at my email field. Isn't Django supposed to validate this and give an ValidationError if I use unique=True in my model? Or do I have to Try and Catch the IntegrityError myself?
Or maybe show me a best practice for validating unique users inside a form/model.
models.py
class Customer(models.Model):
FirstName = models.CharField(max_length=50)
LastName = models.CharField(max_length=50)
Email = models.CharField(max_length=50, unique=True, error_messages={'unique':"This email is already in use"})
views.py
def customerform(request):
if request.method == 'POST':
form = CustomerForm(request.POST)
if form.is_valid():
post = Customer()
post.FirstName = form.cleaned_data['FirstName']
post.LastName = form.cleaned_data['LastName']
post.Email = form.cleaned_data['Email']
post.save()
return render(request, 'results.html', {
'FirstName': form.cleaned_data['FirstName'],
'Email': form.cleaned_data['Email'],})
else:
form = CustomerForm()
return render(request, 'form.html', {'form':form})
forms.py
class CustomerForm(forms.Form):
FirstName = forms.CharField (label='First name:', max_length=50)
LastName = forms.CharField (label='Last name:', max_length=50)
Email = forms.EmailField(label='Email:', max_length=50)
form.html
<form action="/customer/" method="post">
{% csrf_token %}
{{ form }}
<input type="submit" value="Submit">
</form>
If you want form validation to automatically use the model attributes, you have to use a ModelForm:
class CustomerForm(forms.ModelForm):
class Meta:
model = Customer
fields = ["FirstName", "LastName", "Email"]
If you want to use a regular Form, you need to do the validation manually.
def customerform(request):
if request.method == 'POST':
form = CustomerForm(request.POST)
if form.is_valid():
# first we check if email is valid
customer = Customer.objects.filter(Email = form.cleaned_data['Email'])
if customer.count() == 0: # email not in use
post = Customer()
post.FirstName = form.cleaned_data['FirstName']
post.LastName = form.cleaned_data['LastName']
post.Email = form.cleaned_data['Email']
post.save()
return render(request, 'results.html', {
'FirstName': form.cleaned_data['FirstName'],
'Email': form.cleaned_data['Email'],})
else: # email in use so we redirect to html and we add an error message
render(request, 'form.html', {'form':form,'error','This email is already in use'})
else:
form = CustomerForm()
return render(request, 'form.html', {'form':form})
<form action="/customer/" method="post">
{% if error %}
<b> {{ error }} </b> <br>
{% endif %}
{% csrf_token %}
{{ form }}
<input type="submit" value="Submit">
</form>
Related
Django : Why is my submit interest form not submitted. Did I write my view or template wrongly?
Based on the view I wrote, I dont even get redirected after I click on submit. And when i return to the home page, i Receive a message from here
messages.warning(request, 'Something went wrong. Please try again..', extra_tags='wronginterest')
which is why I believe it is because the form is not valid thats why it is not submitting. but wth why is it not valid?? Thanks
views.py
def submit_interest_view(request, slug):
user = request.user
blog_post = BlogPost.objects.get(slug=slug)
num_blogpost = BlogPost.objects.filter(author=request.user).count()
if not user.is_authenticated:
return redirect('must_authenticate')
elif blog_post.author == request.user:
return HttpResponse('You cannot submit interest to your own post.')
interest_requests = Interest.objects.filter(interestsender=request.user, interestreceiver=blog_post.author)
for interest_request in interest_requests:
if interest_request.is_active:
return HttpResponse('You have already submitted your interest to this post.')
if request.method == 'POST': # use request.method == 'POST' to submit POST request (like submitting a form)
form = SubmitInterestForm(request.POST, request.FILES)
if form.is_valid():
obj = form.save(commit=False)
author = Account.objects.get(email=user.email) # use get if you aim to get a single object not a queryset
obj.author = author
obj.blog_post = blog_post
obj.save()
messages.success(request, 'Your interests have been submitted', extra_tags='submittedinterest')
context['success_message'] = "Updated"
if request.META.get('HTTP_REFERER') == request.build_absolute_uri(reverse("HomeFeed:main")):
return redirect(reverse("HomeFeed:main"))
elif request.META.get('HTTP_REFERER') == request.build_absolute_uri(reverse("HomeFeed:detail", kwargs={'slug': slug })):
return redirect(reverse('HomeFeed:detail', kwargs={'slug': slug}))
else:
return redirect(reverse('HomeFeed:main'))
#return redirect(reverse('HomeFeed:detail', kwargs={'slug': slug})) # redirect to your post detail but use reverse to pass kwargs not just redirect
else:
messages.warning(request, 'Something went wrong. Please try again..', extra_tags='wronginterest')
else:
form = SubmitInterestForm() # if request.method isnt POST you still need to define your form so it can be displayed
return render(request, "HomeFeed/submitinterest.html", {'form': form,'user': user, 'num_blogpost': num_blogpost, 'blog_post': blog_post}) # context dict
forms.py
HomeFeed: forms.py:
class SubmitInterestForm(forms.ModelForm):
class Meta:
model= Interest
fields = ['my_name', 'my_thoughts','short_file',]
models.py
class Interest(models.Model):
interestsender = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='interestsender', on_delete=models.CASCADE)
interestreceiver = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='interestreceiver', on_delete=models.CASCADE)
timestamp = models.DateTimeField(auto_now_add=True)
is_active = models.BooleanField(blank=False, null=False, default=True)
my_name = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
blog_post = models.ForeignKey(BlogPost, on_delete=models.CASCADE)
my_thoughts = models.TextField(max_length=5000, null=False, blank=False)
short_file = models.FileField(upload_to='documents/', null=True, blank=True)
def upload_location(instance, filename):
#you define this variable called file_path. It belongs to the HomeFeed app, and takes in the parameter of author id, title of blog post with the file name that the author uploads it, and you want to format it
file_path = 'HomeFeed/{author_id}/{title}-{filename}'.format(
author_id=str(instance.author.id),title=str(instance.chief_title), filename=filename)
#the above will let you insert the strings, you want to take ID of the user who is uploading and converting it into a string, and also the title and file name, converting them into string
# return file path means where the images is stored, either the local machine/ production environment which will be the name file stored in the content delivery network
return file_path
class BlogPost(models.Model):
chief_title = models.CharField(max_length=50, null=False, blank=False)
body = models.TextField(max_length=5000, null=False, blank=False)
likes = models.ManyToManyField(settings.AUTH_USER_MODEL, related_name='blog_posts', blank=True)
author = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
slug = models.SlugField(blank=True, unique=True)
date_published = models.DateTimeField(auto_now_add=True, verbose_name="date published")
class Account(AbstractBaseUser):
email = models.EmailField(verbose_name="email", max_length=60, unique=True)
username = models.CharField(max_length=30, unique=True)
date_joined = models.DateTimeField(verbose_name='date joined', auto_now_add=True)
last_login = models.DateTimeField(verbose_name='last login', auto_now=True)
is_admin = models.BooleanField(default=False)
is_active = models.BooleanField(default=True)
is_staff = models.BooleanField(default=False)
is_superuser = models.BooleanField(default=False)
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['username']
html:
submit_interest.thml
<form method="post" enctype="multipart/form-data">{% csrf_token %}
<div class="form-group">
<input class="form-control bg-white form-control-plaintext c" rows="10" type="text" name="my_name" id="my_name" placeholder="Name: {{ request.user.username }}" readonly></input>
</div>
<div class="form-group">
<label for="my_thoughts text-dark">thoughts:</label>
<textarea class="form-control" rows="6" type="text" name="my_thoughts" id="my_thoughts" placeholder="My thoughts..." required></textarea >
</div>
<label for="short_file " class="text-dark">Brief file (Optional):</label>
<input type="file" class="btn btn-md btn-light" name="short_file" id="short_file"> <!--<button type="submit" class="btn btn-md btn-info">Upload</button>-->
{% if uploaded_file_url %}
<p>File uploaded at: {{ uploaded_file_url }}</p>
{% endif %}
<button class="submit-button btn btn-lg btn-primary mt-3 btn-block col-lg-6 offset-lg-3 " type="submit">Submit Interest</button>
</form>
urls.py
app_name = 'HomeFeed'
urlpatterns = [
path('', home_feed_view , name= "main"),
path('submitinterest/<slug>', submit_interest_view, name= "submitinterest"),
You need to find out why this is not validating. When the form is not valid, send the form instance back to template via context and try to render the errors. For example:
# view
if form.is_valid():
# ...
else:
messages.warning(request, 'Something went wrong. Please try again..', extra_tags='wronginterest')
return render(request, "HomeFeed/submitinterest.html", {'form': form,'user': user, 'num_blogpost': num_blogpost, 'blog_post': blog_post}) # context dict
# template
{% for field in form %}
<div class="fieldWrapper">
{{ field.errors }}
{{ field.label_tag }} {{ field }}
{% if field.help_text %}
<p class="help">{{ field.help_text|safe }}</p>
{% endif %}
</div>
{% endfor %}
For more details on form error rendering, please see the documentation.
BTW some minor optimisation on your code:
from django.contrib.auth.decorators import login_required
from django.shortcuts import get_object_or_404
#login_required
def submit_interest_view(request, slug):
user = request.user
blog_post = get_object_or_404(BlogPost, slug=slug)
num_blogpost = BlogPost.objects.filter(author=user).count()
if blog_post.author.email == user.email:
return HttpResponse('You cannot submit interest to your own post.')
interest_requests = Interest.objects.filter(interestsender=user, interestreceiver=blog_post.author, is_active=True)
if interest_requests.exists():
return HttpResponse('You have already submitted your interest to this post.')
if request.method == 'POST': # use request.method == 'POST' to submit POST request (like submitting a form)
form = SubmitInterestForm(request.POST, request.FILES)
if form.is_valid():
obj = form.save(commit=False)
author = Account.objects.get(email=user.email) # use 'author = user.account' if there is OneToOne relation between user and account
obj.author = author
obj.blog_post = blog_post
obj.save()
messages.success(request, 'Your interests have been submitted', extra_tags='submittedinterest')
context['success_message'] = "Updated"
if request.META.get('HTTP_REFERER') == request.build_absolute_uri(reverse("HomeFeed:main")):
return redirect(reverse("HomeFeed:main"))
elif request.META.get('HTTP_REFERER') == request.build_absolute_uri(reverse("HomeFeed:detail", kwargs={'slug': slug })):
return redirect(reverse('HomeFeed:detail', kwargs={'slug': slug}))
else:
return redirect(reverse('HomeFeed:main'))
#return redirect(reverse('HomeFeed:detail', kwargs={'slug': slug})) # redirect to your post detail but use reverse to pass kwargs not just redirect
else:
messages.warning(request, 'Something went wrong. Please try again..', extra_tags='wronginterest')
return render(request, "HomeFeed/submitinterest.html", {'form': form,'user': user, 'num_blogpost': num_blogpost, 'blog_post': blog_post})
else:
form = SubmitInterestForm() # if request.method isnt POST you still need to define your form so it can be displayed
return render(request, "HomeFeed/submitinterest.html", {'form': form,'user': user, 'num_blogpost': num_blogpost, 'blog_post': blog_post}) # context dict
I have to insert the post through a form but when i submit i got this error FOREIGN KEY constraint failed, the problem is with the author field
models.py
class Post(models.Model):
STATUS_CHOICES = (
('draft','Draft'),
('published','Published'),
)
title = models.CharField(max_length=100)
slug = models.SlugField(max_length=120)
author = models.ForeignKey('auth.User',related_name='blog_posts',on_delete=models.CASCADE,blank=True, null=True)
body = RichTextField()
created = models.DateTimeField(auto_now=True)
status = models.CharField(max_length=10,choices=STATUS_CHOICES,default='draft')
tag = models.OneToOneField(Tag,related_name="blog_tag",on_delete=models.CASCADE,default=0)
def __str__(self):
return self.title
views.py
def tagView(request,name):
tag = Tag.objects.get(name=name)
post_form = PostForm(request.POST or None)
if request.method == 'POST':
post_form = PostForm(request.POST)
if post_form.is_valid():
item = post_form.save(commit=False)
item.author = request.user
item.save()
return HttpResponseRedirect(request.path_info)
context = {
'post_form' : post_form,
'tag' : tag,
}
return render(request,'blog/tagPage.html',context)
forms.py
class PostForm(forms.ModelForm):
class Meta:
model = Post
fields = ['title','body']
template
<form class="post-form" method="POST" enctype="multipart/form-data" action="">
{% csrf_token %}
{{ post_form }}
<input class="post-form-submit submit" type="submit" value="Save">
</form>
If the author field is the problem, be sure to import auth.User
My guess is that you are trying to add the post as anonymous but anonymous user is not null (id is though). Also don't use 'auth.User' and follow the instructions from the documentation.
Hello everyone thanks for your help.
I have made a sign up form with django. As of now when i fill in all of the fields in the form it allows me to submit, the new user is created, then i get the error below
UNIQUE constraint failed: slug_trade_app_userprofile.user_id
Also in my models (below) i have a onetoone relations with the UserProfile model and for some reason none of that data is stored to the database, it only gets the username/pass/first/last. It appears to me that for some reason it submits the form the trys to submit again causing the UNIQUE issue. Not sure what i did wrong here, thanks for your help.
Blow is my code
views.py
def signup(request):
signed_in = False
if request.method == "POST":
user_form = UserForm(data=request.POST)
profile_form = UserProfileForm(request.POST)
print(user_form.is_valid())
print(profile_form.is_valid())
if user_form.is_valid() and profile_form.is_valid():
user = user_form.save()
user.set_password(user.password)
user.save()
profile = profile_form.save(commit=False)
profile.user = user
if 'profile_picture' in request.FILES:
profile.profile_picture = request.FILES['profile_picture']
profile.save()
signed_in = True
else:
print(user_form.errors, profile_form.errors)
else:
user_form = UserForm()
profile_form = UserProfileForm()
return render(request, 'my_app/signup.html',
{'user_form':user_form,
'profile_form':profile_form,
'signed_in':signed_in})
models.py
class UserProfile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
profile_picture = models.ImageField(upload_to='static/profile_pictures', blank=True )
bio = models.TextField(max_length=500, blank=True)
on_off_campus = models.CharField(max_length=3,
default="on",
choices=CAMPUS_STATUS)
forms.py
class UserForm(forms.ModelForm):
password = forms.CharField(widget=forms.PasswordInput())
class Meta():
model = User
fields = ('username','first_name', 'last_name', 'email', 'password')
class UserProfileForm(forms.ModelForm):
class Meta():
model = UserProfile
fields = ('profile_picture', 'bio', 'on_off_campus')
signup.html
{% if registered %}
<h1>Thank you for registering!</h1>
{% else %}
<h2>Sign Up</h2>
<form enctype="multipart/form-data" method="post" >
{% csrf_token %}
{{ user_form.as_p }}
{{ profile_form.as_p }}
<input type="submit" name="" value="Sign Up">
</form>
{% endif %}
I have a Post model for users submitting posts. I've given the content field of Post an attribute of blank=True. But for some reason django tells me content is still required. form_post.errors prints this:
<ul class="errorlist"><li>content<ul class="errorlist"><li>This field is required.</li></ul></li></ul>
Here's my code:
models
class Post(models.Model):
...
user = models.ForeignKey(User, blank=True, null=True)
title = models.TextField(max_length=76)
content = models.TextField(null=True, blank=True)
category = models.CharField(max_length=20, choices=CATEGORY_CHOICES, default='1')
forms
class PostForm(forms.ModelForm):
content = forms.CharField(widget=PagedownWidget)
title = forms.TextInput(attrs={'placeholder': 'title'})
class Meta:
model = Post
fields = [
'title',
'content',
'category',
'image',
'id',
'user'
]
views
def post(request):
allauth_login = LoginForm(request.POST or None)
allauth_signup = SignupForm(request.POST or None)
if request.user.is_authenticated():
form_post = PostForm(request.POST or None, request.FILES or None)
if form_post.is_valid():
print('valid')
instance = form_post.save(commit=False)
instance.user = request.user
category = form_post.cleaned_data['category']
for a, b in CATEGORY_CHOICES:
if a == category:
category = b
form_post.save()
return HttpResponseRedirect('/%s' % category)
else:
print(form_post.errors)
form_post = PostForm()
context = {
'allauth_login': allauth_login,
'allauth_signup': allauth_signup,
'form_post': form_post
}
return render(request, 'post.html', context)
else:
return HttpResponseRedirect("/accounts/signup/")
html
...
<form method="post" action="" enctype="multipart/form-data">{% csrf_token %}
<div class="submitContainer">
<div class="article_title_div">
{{ form_post.title|add_class:"article_title" }}
</div>
<div>
</div>
{{ form_post.category }}
</div>
<div class="submitButton">
<button class="submitArticleSubmit" type="submit">Post</button>
</div>
</form>
...
Any idea why I'm getting this error?
The reason for this, is because you're overriding the default model field. Both content and title.
Although, content can be nullable when stored in your database, it is required by your form (content = forms.CharField(widget=PagedownWidget)).
Change to content = forms.CharField(widget=PagedownWidget, required=False) to make it optional on form submission.
I created a form based on several ModelForm elements. All fields work fine excluding the
ImageField. Due to the missing ImageField form.is_valid() always returns False - even though I pass request.FILES to form.
Why the form with the ImageField is always invalid / empty?
Forms
class UserProfileForm2(forms.ModelForm):
class Meta:
model = models.UserProfile
fields = ['description', 'picture']
class LocationForm(forms.ModelForm):
class Meta:
model = models.Location
fields = ['city', 'state', 'country']
class UserForm(forms.ModelForm):
class Meta:
model = registration_models.User
fields = ['first_name', 'last_name']
Models
class Location(models.Model):
city = models.CharField(max_length=100)
state = models.CharField(max_length=100)
country = models.CharField(max_length=100)
def __unicode__(self):
return ' - '.join([self.city, self.state, self.country])
class UserProfile(models.Model):
authenticationuser = fields.AutoOneToOneField(AuthUser)
description = models.TextField()
picture = models.ImageField(upload_to='uploaded_files/', null=True)
location = models.ForeignKey(Location, null=True)
appear_in_public_ranking = models.BooleanField(default=True)
def __unicode__(self):
return self.authenticationuser.username
View
#login_required
def changeprofile(request):
form = None
# user posted his new profile settings
if request.method == 'POST':
user_form = myforms.UserForm(request.POST)
user_profile_form = myforms.UserProfileForm2(request.POST, request.FILES)
location_form = myforms.LocationForm(request.POST)
forms_are_invalid = not (user_form.is_valid() and user_profile_form.is_valid() and not location_form.is_valid())
if forms_are_invalid:
forms = {'user_form':user_form,
'user_profile_form':user_profile_form,
'location_form':location_form}
return shortcuts.render(request, 'changeprofile.html', forms)
location_form.save()
user_form.save()
user_profile_form.save()
return HttpResponseRedirect('/profile')
else:
forms = {'user_form':user_form,
'user_profile_form':user_profile_form,
'location_form':location_form}
return shortcuts.render(request, 'changeprofile.html', forms)
Template
<form action="{% url 'changeprofile' %}" method="post">
{% csrf_token %}
{{ user_form }}
{{ location_form }}
{{ user_profile_form }}
<input type="submit" value="Submit" />
</form>
If your form contains file input then you must set enctype in your form as:
<form action="{% url 'changeprofile' %}" method="post" enctype="multipart/form-data">
Otherwise request.FILES will always be empty.