Django form validation failing - pytz and choices - python

I am trying to let the user set which timezone they are in, however form validation .is_valid() is failing and cannot figure out why.
The timezone value for a user is stored in a Profile model.
Using ChoiceField and pytz.common_timezones to fill the form field
This would be appear to be quite simple to do, the only thing thats different to my usual way is that the data filling the combo/select box is the use of a ChoiceField and the data is coming from pytz.
I may switch to django-timezone-field to solve this, but I would like to understand why it is failing. I have included all relevant (I think) code below. Any suggestions?
models.py
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
bio = models.TextField(max_length=500, blank=True)
location = models.CharField(max_length=30, blank=True)
birth_date = models.DateField(null=True, blank=True)
timezone = models.CharField(
max_length=255,
blank=True,
)
forms.py
class ProfileEditForm(forms.Form):
profile_timezone = forms.ChoiceField(choices=[(x, x) for x in pytz.common_timezones])
views.py
#login_required
def userprofile_edit(request):
if request.method == "POST":
profile_edit_form = ProfileEditForm()
if profile_edit_form.is_valid():
cd = profile_edit_form.cleaned_data
user = User.objects.get(id=request.user.id)
user.profile.timezone = cd['timezone']
user.profile.save()
messages.success(request, "Profile updated successfully", fail_silently=True)
return redirect('coremgr:userprofile', request.user.id)
else:
messages.error(request, "Error occured. Contact your administrator", fail_silently=True)
print "error: form not valid"
else:
profile_edit_form = ProfileEditForm()
context = {
'profile_edit_form': profile_edit_form,
}
return render(request, 'apps/coremgr/userprofile_edit.html', context)
template
<form name="formprofile" method="POST" action="">
{% csrf_token %}
<p id="profile_timezone" class="form-inline">
{{ profile_edit_form.profile_timezone.errors }}
Timezone:
{{ profile_edit_form.profile_timezone }}
</p>
<button id="id_btn_profile_edit_save" type="submit" class="btn btn-default" tabindex=7>Save</button>
</form>

add request.POST in form
if request.method == "POST":
profile_edit_form = ProfileEditForm(request.POST)

I believe you would need to pass in request.POST when initializing the form in your if block.

Related

Django database relation

I am working on a forum using django, I have a problem accessing user fullname and bio, from a model class I have. I have no problem accessing the user.username or user.email, but not from the Author class..
This is from the models.py in the forum app
User = get_user_model()
class Author(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
fullname = models.CharField(max_length=40, blank=True)
slug = slug = models.SlugField(max_length=400, unique=True, blank=True)
bio = HTMLField()
points = models.IntegerField(default=0)
profile_pic = ResizedImageField(size=[50, 80], quality=100, upload_to="authors", default=None, null=True, blank=True)
def __str__(self):
return self.fullname
My form is in the user app, where i have a profile update site, and the form is like this
from forums.models import Author
class UpdateForm(forms.ModelForm):
class Meta:
model = Author
fields = ('fullname', 'bio', 'profile_pic')
Then here is some of the update site, however nothing let me get access to the bio or fullname, I've tried so many combos. and I am lost here..
{% block content %}
<section class="section" id="about">
<!-- Title -->
<div class="section-heading">
<h3 class="title is-2">Hey {{ user.username }}</h3>
<div class="container">
<p>{{ user.bio }}bio comes here</p>
</div>
</div>
Here is the view.py from the user app
from .forms import UpdateForm
def update_profile(request):
context = {}
user = request.user
instance = Author.objects.filter(user=user).first()
if request.method == 'POST':
form = UpdateForm(request.POST, request.FILES, instance=user)
if form.is_valid():
form.instance.user = user
form.save()
return redirect('/')
else:
form = UpdateForm(instance=user)
context.update({
'form': form,
'title': 'update_profile',
})
return render(request, 'register/update.html', context)
The form html
<form method="POST" action="." enctype="multipart/form-data">
{% csrf_token %}
{{form|crispy}}
<hr>
<button class="button is-block is-info is-large is-fullwidth">Update <i class="fa fa-sign-in" aria-hidden="true"></i></button>
</form>
If there is some relation i am missing please help
Your view currently creates a new Author record each time you "update" the model. I would advise to first clean up the database and remove all authors.
Then you can convert the ForeignKey into a OneToOneField here: that way we know that each user has at most one Author:
from django.conf import settings
class Author(models.Model):
user = models.OneToOneField(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE
)
# …
Now we can alter the view to create a record in case there is no such record, or update an existing record if there is one:
from .forms import UpdateForm
def update_profile(request):
context = {}
user = request.user
instance = Author.objects.filter(user=user).first()
if request.method == 'POST':
form = UpdateForm(request.POST, request.FILES, instance=instance)
if form.is_valid():
form.instance.user = user
form.save()
return redirect('/')
else:
form = UpdateForm(instance=instance)
context.update({
'form': form,
'title': 'update_profile',
})
return render(request, 'register/update.html', context)
In the template, you can render data of the related Author model for a user user with:
{{ user.author.fullname }}
{{ user.author.bio }}

unable to upload files in django through web forms

I am trying to create a django website similar to that of Udemy or Coursera. I am trying to implement a feature that lets users add courses.
Here is my view for adding the course:
def create_course(request):
form = CreateCourseForm()
if request.method == 'POST':
form = CreateCourseForm(request.POST)
if form.is_valid():
new_course = form.save(commit=False)
new_course.instructor = request.user
new_course.save()
return redirect('home')
return render(request, 'courses/create.html',{'form':form})
Here is my form:
class CreateCourseForm(forms.ModelForm):
class Meta:
model = Course
fields = ('name','video','description','category',)
My course model:
class Course(models.Model):
name = models.CharField(max_length=200)
instructor = models.ForeignKey(User, on_delete=models.CASCADE)
video = models.FileField(upload_to='videos/')
description = models.TextField()
category = models.ForeignKey(Category, on_delete=models.CASCADE)
def __str__(self):
return self.name
And finally my web form:
{% extends "base.html" %}
{% load crispy_forms_tags %}
{% block content %}
<h1>Create a Course</h1>
<hr>
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
{{ form|crispy }}
<button class="btn btn-sm btn-outline-primary" type="submit">Create Course</button>
</form>
{% endblock %}
The issue i am facing is that when i try to create a course, the video field shows an error "This field is required" even when I have uploaded a video.
I tried researching a bit and found out that adding enctype="multipart/form-data" to the form was required, but even adding that didn't solve my problem
Can anyone help me here?
You need to pass request.POST and request.FILES to the form, so:
def create_course(request):
form = CreateCourseForm()
if request.method == 'POST':
# add request.FILES &downarrow;
form = CreateCourseForm(request.POST, request.FILES)
# …
return render(request, 'courses/create.html',{'form':form})
If you only pass request.POST, you are only passing the items of the form that are not file uploads. This then will indeed add an error to the form fields of these files, saying that the form requires data for these fields.
as willem told you have to request file in you form like this
def create_course(request):
form = CreateCourseForm()
if request.method == 'POST':
form = CreateCourseForm(request.POST, request.FILES)
if form.is_valid():
new_course = form.save(commit=False)
new_course.instructor = request.user
new_course.save()
return redirect('home')
return render(request, 'courses/create.html',{'form':form})
but i think you have to know one more you don't have to pass / in while telling django where to upload that file
class Course(models.Model):
name = models.CharField(max_length=200)
instructor = models.ForeignKey(User, on_delete=models.CASCADE)
video = models.FileField(upload_to='videos')
description = models.TextField()
category = models.ForeignKey(Category, on_delete=models.CASCADE)
def __str__(self):
return self.name
offcourse willem answer will solve your problem but you will see in your media folder a new folder name videos/ got created where you file is storing instead of your actual folder becuase you are telling django to upload that file in folder name vedios/ and if django doesn't find that folder it will create that folder with that name and then stat uploading it

Does not reflect current user in comment

I'm creating an Instagram-style application with Django 3.2.5, where users have profiles to visit and upload photos. I am implementing a commenting system.
When generating the comment as a user with a registered profile, the comment is rendered as if it were anonymous.
I have investigated and it happens that in the model of the comment I made a relationship with the user and profile giving the null parameter as true.
When removing this parameter and resubmitting a comment on a post I get a Not Null Constraint Failed error for profile_id and for user_id.
The image shows the first comments made from the admin. And the last one with the null = true parameter in the model.
Sample picture
models.py
class Comment(models.Model):
post = models.ForeignKey(Post, on_delete=models.CASCADE, related_name='comments')
user = models.ForeignKey(User, on_delete=models.CASCADE)
profile = models.ForeignKey(Profile, on_delete=models.CASCADE)
text = models.TextField(max_length=200)
created_date = models.DateTimeField(auto_now_add=True)
class Meta:
ordering = ['created_date']
def __str__(self):
return self.text
views.py
def create_comment(request, pk):
post = get_object_or_404(Post, pk=pk)
if request.method == "POST":
form = CommentForm(request.POST)
if form.is_valid():
comment = form.save(commit=False)
comment.post = post
comment.save()
return redirect('posts:feed')
else:
form = CommentForm()
return render(request, 'posts/feed.html', {
'form': form,
})
urls.py
urlpatterns = [
path(r'^post/(?P<pk>[0-9]+)/comment/$', create_comment, name='create'),]
forms.py
class CommentForm(forms.ModelForm):
pass
class Meta:
model = Comment
fields = ['text']
HTML
<form action="{% url 'comments:create' pk=post.pk %}" method="POST" >
{% csrf_token %}
<div class="input-group">
<input type="text" class="form-control" placeholder="Comment here" aria-label="text" name="text" value="{{ post.comment.pk}}" >
<div class="input-group-append">
<button class="btn btn-dark" type="submit" id="button-addon2"><i class="fas fa-comment"></i></button>
</div>
</div>
</form>
You should link the .user attribute to the logged in user, so:
from django.contrib.auth.decorators import login_required
#login_required
def create_comment(request, pk):
if request.method == 'POST':
form = CommentForm(request.POST, request.FILES)
if form.is_valid():
form.instance.post_id = pk
form.instance.user = request.user
form.save()
return redirect('posts:feed')
else:
form = CommentForm()
return render(request, 'posts/feed.html', {'form': form})
By making use of .post_id = pk, we no longer query the database to obtain the post first, validation is done when creating the Comment instance.
Note: It is normally better to make use of the settings.AUTH_USER_MODEL [Django-doc] to refer to the user model, than to use the User model [Django-doc] directly. For more information you can see the referencing the User model section of the documentation.
Note: You can limit views to a view to authenticated users with the
#login_required decorator [Django-doc].

How to autofill editable forms in Django

tl;dr How to autofill an editable form with information stored in database
Hey, Im creating a profile page for an application using Django as a framework. And Im having some annoying issues when a user is editing their page. As it is now, the user has to retype every field in the form, to edit a single field.. Cause my view has to delete the previous information in each field, or I get some annoying errors.
So my question is, is there a way to autofill these fields in profile_edit.html with the strings corresponding to each field in the form, from the database?
Any help would be greatly appreciated :D
view.py
#login_required
def profile_edit(request):
form = ProfileUpdateForm(request.POST, request.FILES)
if request.method == 'POST':
if form.is_valid():
user = request.user
if 'image' in request.FILES:
user.profile.image = request.FILES['image']
user.profile.bio = form.cleaned_data.get("bio")
user.profile.birth_date = form.cleaned_data.get("birth_date")
user.profile.location = form.cleaned_data.get("location")
user.save()
return redirect('profile')
else:
form = ProfileUpdateForm()
context = {
'form' : form
}
return render(request, 'webside/profile_edit.html', context)
models.py
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
bio = models.TextField(max_length=500, blank=True)
location = models.CharField(max_length=30, blank=True)
birth_date = models.DateField(null=True, blank=True)
email_confirmed = models.BooleanField(default=False)
image= models.FileField(upload_to='profile_image/', blank = True)
def __str__(self):
return self.user.username
profile_edit.html
'{% csrf_token %}
{% for field in form %}
<p>
{{ field.label_tag }}<br>
{{ field }}
{% for error in field.errors %}
<p style="color: red">{{ error }}</p>
{% endfor %}
</p>
{% endfor %}'
pic of profile.html
forms.py
class ProfileUpdateForm(forms.ModelForm):
YEARS= [x for x in range(1900,2021)]
birth_date = forms.DateField( initial="21-06-1995", widget=forms.SelectDateWidget(years=YEARS))
class Meta:
model = Profile
fields = ('bio','birth_date','location','image')
The way you initialise your form in your view is all wrong:
def profile_edit(request):
user = request.user
# form = ProfileUpdateForm(request.POST, request.FILES) <-- remove
if request.method == 'POST':
form = ProfileUpdateForm(request.POST, request.FILES, instance=user.profile)
if form.is_valid():
form.save() # <-- you can just save the form, it will save the profile
# user.save() <-- this doesn't help you, it doesn't save the profile and since user isn't changed you don't need to save it!
return redirect(...)
# else:
# form = ProfileUpdateForm() <-- don't clear the form!
else: # GET
form = ProfileUpdateForm(instance=user.profile) <-- initialise with instance
context = {
'form' : form
}
return render(request, 'webside/profile_edit.html', context)
You need to add the instance to the form to update an existing instance. You shouldn't initialise an empty form if the form is not valid, because that means the user loses all the data if they made a mistake. You want to display the form with all the data and the errors in that case.

Edit UserProfile using Django Forms

I am creating a web application using the Django web framework. As part of the application, I wanted to give users the ability to update their profile as they choose. When a user tries to update their profile however, the information remains the same even after the user has submitted new information. I've checked my database and the values are not being updated in there either. It is possible however, to change a Users information on the admin page. Below is how I have tried to implement the functionality.
Model
class UserProfileModel(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
age = models.PositiveIntegerField(blank=True, null=True)
email = models.EmailField(max_length=254, null=True, blank=True, unique=True)
height = models.PositiveIntegerField(blank=True, null=True)
weight = models.PositiveIntegerField(blank=True, null=True)
Form
class UpdateProfile(forms.ModelForm):
class Meta:
model = UserProfileModel
fields = ('email', 'age', 'height', 'weight')
Views
def update_profile(request):
args = {}
if request.method == 'POST':
form = UpdateProfile(request.POST, instance=request.user)
if form.is_valid():
form.save()
# return HttpResponseRedirect(reverse('account:profile'))
return render(request, 'account/profile.html')
else:
form = UpdateProfile()
if request.user.is_authenticated():
form = UpdateProfile(instance=request.user)
args['form'] = form
return render(request, 'account/edit_profile.html', args)
Url
app_name = 'account'
urlpatterns = [
url(r'^profile/$', views.profile, name='profile'),
url(r'^profile/edit/$', views.update_profile, name='edit_profile'),
url(r'^home/$', views.home, name='home'),
]
HTML
{% block body %}
<div class="container">
<form method="POST" action=".">
{% csrf_token %}
{{ form.as_p }}
<button type="submit">Submit</button>
</form>
<br>
</div>
{% endblock %}
Your form is not for a User, it is for a UserProfileModel (which by the way is an odd name; there's no need to include the word "model" there).
So you should pass the profile, not the user, to the form:
form = UpdateProfile(request.POST, instance=request.user.userprofilemodel)
Note, you probably also want to pass it in the else block, so that the user sees the existing data to edit on GET:
form = UpdateProfile(instance=request.user.userprofilemodel)
Also, you should redirect after successful POST, as you do in the commented-out line, rather than rendering a template.

Categories

Resources