Extra form fields added in Django form render - python

I've got a django form setup as follows:
forms.py
class TestimonialForm(forms.ModelForm):
name = forms.CharField(widget=forms.TextInput(attrs={'class': 'form-control'}))
designation = forms.CharField(widget=forms.TextInput(attrs={'class': 'form-control'}))
testimonial = forms.CharField(widget=forms.Textarea(attrs={'class': 'form-control test-form-area'}))
class Meta:
model = Testimonials
models.py
class Testimonials(models.Model):
name = models.CharField(max_length=128)
test = models.CharField(max_length=2000)
credentials = models.CharField(max_length=128)
def __unicode__(self):
return self.name
views.py
def add_testimonial(request):
context = RequestContext(request)
if request.method == 'POST':
form = TestimonialForm(request.POST)
if form.is_valid():
form.save(commit=True)
return index(request)
else:
print form.errors
else:
form = TestimonialForm()
return render_to_response('/add_testimonial.html', {'form': form}, context)
On render, I see the three main fields that I defined in forms.py that have the classes form-control added to them. However, I also see two extra fields that have no styling attached to them. Any idea what's going wrong?

Your form defines the fields name, designation and testimonial, but the model provides name, test and credentials. So the name field gets redefined with the extra styling, but the other two fields are simply added to the default fields provided by the model. If you want to override them, you need to use the same names.

Related

How to automatically get user in django admin through form

I have a form in my django website where the user requests coins and the information is sent to the admin for me to process. I want to automatically get the user who filled the form without them doing it themselves.
Here's the model.py file:
class Requestpayment (models.Model):
username= models.ForeignKey(User, on_delete= models.CASCADE, null=True)
useremail= models.CharField(max_length=100)
accountmail= models.CharField(max_length=100)
accountphonenumber=models.CharField(max_length=15)
coinsrequested=models.ForeignKey(Requestamount, on_delete= models.SET_NULL, null=True)
created= models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.accountmail
the forms.py:
class Requestpaymentform (ModelForm):
class Meta:
model = Requestpayment
fields = '__all__'
and the views.py:
#login_required(login_url='login')
def redeemcoins (request):
form = Requestpaymentform
if request.method =='POST':
form = Requestpaymentform(request.POST)
if form.is_valid():
form = form.save(commit=False)
username = request.user
form.save()
return redirect ('home')
I am pretty sure something is wrong but i don't know what it is (I'm very new at django) anyway the form always shows all the users in the website for the current user to pick who they are.
redeem coins page
I also tried excluding that part of the form but it didn't work it just shows up empty in the admin.
thank you.
You need to assign it to the instance wrapped in the form, so:
#login_required(login_url='login')
def redeemcoins(request):
form = Requestpaymentform()
if request.method == 'POST':
form = Requestpaymentform(request.POST)
if form.is_valid():
form.instance.username = request.user
form.save()
return redirect('home')
# …
It makes more sense however to name this field user than username. In the model you can also make the username field non-editable, such that it does not appear in the form:
from django.conf import settings
class Requestpayment(models.Model):
user = models.ForeignKey(
settings.AUTH_USER_MODEL, editable=False, on_delete=models.CASCADE
)
# …
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.
When you use username= models.ForeignKey(User, on_delete= models.CASCADE, null=True), Django add a field named user_id in your database which allow django to find User object for Requestpayment.
You can use user_id field to add a User object in Requestpayment.
You don't need to pass username field in your fields list if you want to get user in view.
class Requestpaymentform (ModelForm):
class Meta:
model = Requestpayment
#fields = '__all__'
fields = ['useremail',
'accountmail',
'accountphonenumber',
'coinsrequested',
'created']
Now do this to get user in your view.
#login_required(login_url='login')
def redeemcoins(request):
form = Requestpaymentform()
if request.method == 'POST':
form = Requestpaymentform(request.POST)
if form.is_valid():
requestpayment = form.save(commit=False)
requestpayment.user_id = request.user.id
requestpayment.save()
return redirect('home')
And it's great to use user instead username because it's a User object and not a simple field.
Please for my English !!!

Django - how add User specific Items?

Good day Stackoverflow,
a user should be able to add multiple titles instead of always overwriting the one added title.
\\ views.py
def edit_profile(request):
try:
profile = request.user.userprofile
except UserProfile.DoesNotExist:
profile = UserProfile(user=request.user)
if request.method == 'POST':
form = UserProfileForm(request.POST, instance=profile)
if form.is_valid():
form.save()
return redirect('/test')
else:
form = UserProfileForm(instance=profile)
return render(request, 'forms.html', {'form': form, 'profile': profile})
\\models.py
class UserProfile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
title = models.CharField(max_length=1024)
def __str__(self):
return str(self.title)
\\forms.py
class UserProfileForm(forms.ModelForm):
class Meta:
model = UserProfile
fields = ('title',)
Then the user has a form on the website where he can add the specific title.
Until now, however, every time the user fills out the title form, the value in the database is overwritten.
As it should be:
When a new title is added in the form, it should simply be added to it.
At the end I should have the possibility, with a Foor loop in the HTML template, to display all the added titles of the respective user.
Do you know how to do this?
If you are using a relational database, this functionality isn't really supported for a single field. Though, if you really wanted to, you could use a JSON field to make this work.
However, it is probably a better idea to use a separate table for titles.
To do this, you need to create a new Title object like:
class Title(models.Model):
Then, create a many-to-one relationship using ForeignKey:
class Title(models.Model):
text = models.CharField(max_length=1024)
user_profile = models.ForeignKey(UserProfile, on_delete=models.CASCADE)
The on_delete method is required. This particular one will delete all Titles associated with a UserProfile if a UserProfile is deleted.
Now, if you want to associate a title object with a UserProfile, you would do it like this:
profile = UserProfile(user=request.user)
title = Title.objects.create(text='My Very First Title', user_profile=profile)
For more info on many-to-one relationships in Django: https://docs.djangoproject.com/en/4.0/topics/db/examples/many_to_one/
You can create new model and assign new with the ForeignKey field.
models.py:
class UserProfile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
def __str__(self):
return ', '.join([title for title in self.titles.all()])
class UserTitle(models.Model):
title = models.CharField(max_length=1024)
userprofile = models.ForeignKey(UserProfile, on_delete=models.CASCADE, related_name='titles')
def __str__(self):
return self.title
views.py:
def edit_profile(request):
...
if request.method == 'POST':
...
if form.is_valid():
form.instance.userprofile = request.user.userprofile
form.save()
return redirect('/test')
...
admin.py:
from django.contrib import admin
from your_app.models import UserProfile
class UserProfileAdmin(admin.ModelAdmin):
list_display = ['id', 'user', '__str__']
admin.site.register(UserProfile, UserProfileAdmin)
settings.py:
INSTALLED_APPS = [
...
'your_app',
...
In template, to make for loop just use:
{% for title in user.userprofile.titles.all %}
{{ title }}
{% endfor %}
or if you need only User titles in single string:
{{ user.userprofile }}

How to set a permanent value to a Django Form Field

I am building blogsite where authenticated user can add post. The form has three fields including 'user' field (which shows all the user list with a drop down option). The problem is authenticated user can also see other user name.
I have tried two solution
Exclude this field when rendering in template or
whatever the username is chosen the post post will be saved by the name of authenticated user
but the solution I want
'user' field will only show the name of the authenticated user and that will be submitted with title and description
class BlogForm(forms.ModelForm):
class Meta:
model = Blog
fields = '__all__'
view function
if fm.is_valid():
us = fm.cleaned_data['user']
ti = fm.cleaned_data['title']
ds = fm.cleaned_data['desc']
post = Blog(user=us, title=ti, desc=ds)
messages.success(request, 'Blog Created')
post.save()
{% csrf_token %}
{{form.user.label}}{{form.user}}<br><br>
{{form.title.label}}{{form.title}}
{{form.desc.label}} {{form.desc}}
in model form only include "title" and "desc" field so template not render "user" field, we can set user in views.py via dynamically
class BlogForm(forms.ModelForm):
class Meta:
model = Blog
fields = ('title','desc',)
#views.py here we can set user via dynamically user=request.user
if fm.is_valid():
ti = fm.cleaned_data['title']
ds = fm.cleaned_data['desc']
post = Blog(user=request.user, title=ti, desc=ds)
messages.success(request, 'Blog Created')
post.save()
In your Form class
def __init__(self, *args, **kwargs):
super(YourForm, self).__init__(*args, **kwargs)
self.fields['user'].disabled = True
initialise your form:
data = {'user': request.user}
fm = YourForm(initial=data)
next:
if fm.is_valid():
us = fm.cleaned_data['user']
ti = fm.cleaned_data['title']
ds = fm.cleaned_data['desc']
post = Blog(user=us, title=ti, desc=ds)
messages.success(request, 'Blog Created')
post.save()
Your form will get the user from request, so you do not have any dependency for user from form.

Django CharFIeld with unique=True update error "Instance with this Name already exists"

I'm building a Django project for a client that requires me to not user a simple form.save() method to update a model field.
Basically, it looks like this:
I have this model with a CharField who has unique=True:
# models.py
class Course(models.Model):
name = models.CharField(max_length=20, unique=True)
other_field = models.CharField(max_length=10, null=True)
def __str__(self):
return self.name
That model has a form in forms.py:
# forms.py
class CourseCreateForm(forms.ModelForm):
class Meta:
model = Course
fields = ['name', 'other_field']
I need to update this field through a function view (can't be class based in this scenario. Of course, literally it can, but for my student's project requirements it can't be) and I can't use the simple form.save() function, so I need to do the full update code as if it was a normal form:
# views.py
def course_update(request, pk):
course = Course.objects.get(pk=pk)
course_queryset = Course.objects.filter(pk=pk)
if request.method == "POST":
form = CourseCreateForm(request.POST)
if form.is_valid():
name = form.cleaned_data['name']
other_field = form.cleaned_data['other_field']
course_queryset.update(name=name, other_field=other_field) # <-- Where I try to update
else:
print(form.errors)
return HttpResponseRedirect('../')
else:
form = CourseCreateForm(instance=course)
context = {
"form": form,
}
return render(request, 'base/course_update.html', context)
When I try to only update the other_field, the change isn't made and in the formerrors I receive the error "Course with this Name already exists.", so I can't only change the other_field data without also having to change the name field because the name field is unique.
How can you update a model instance's field that has unique=True without changing the unique value?
Hope this makes sense!
Thanks.
UPDATE:
Also wanted to add that it works perfectly fine when unique=True is turned off. I'm just curious how do you update a field normally when unique=True and you're adding in the same variable from a model form.
The reason that this will fail is because the ModelForm thinks you are creating a new record, and thus it will check if an object with the given name already exists, and if so, it of course finds out the name already exists, hence the form is not valid.
You can pass the instance to the form. In that case the form will exclude that item from the unique check:
def course_update(request, pk):
course = Course.objects.get(pk=pk)
course_queryset = Course.objects.filter(pk=pk)
if request.method == 'POST':
form = CourseCreateForm(request.POST, instance=course)
if form.is_valid():
name = name_form.cleaned_data['name']
other_field = course_form.cleaned_data['other_field']
course_queryset.update(name=name, other_field=other_field)
return HttpResponseRedirect('../')
else:
print(form.errors)
else:
form = CourseCreateForm(instance=course)
context = {
'name_form': name_form,
'course_form': course_form,
}
return render(request, 'base/course_update.html', context)

Django: How to prevent model form fields from rendering but include them in validation?

Let's say I have the following model:
class Folder(models.Model):
name = models.CharField(default='untitled', max_length=255)
parent = models.ForeignKey('self', null=True, blank=True)
root = models.ForeignKey('self', null=True, blank=True)
user = models.ForeignKey(settings.AUTH_USER_MODEL)
In my app, this class is used to represents two types of folders: a normal folder-object and a so called root_folder-object, which does not have a parent nor a root-FK set.
I created a custom ModelForm with custom clean(), which is working like a charm (according to unittests and manual testing):
class FolderForm(ModelForm):
def __init__(self, *args, **kwargs):
try:
data = kwargs.get('data', None).copy()
except AttributeError:
data = None
self.prefix = kwargs.get('prefix')
user = kwargs.pop('user', None)
if data is not None:
if user is not None:
data[self.add_prefix('user')] = user.id
kwargs['data'] = data
super(FolderForm, self).__init__(*args, **kwargs)
def clean(self):
# just working fine, so I won't include it here
pass
class Meta:
model = Folder
fields = '__all__'
So, because my root-folder is just a normal Folder-object with blank FKs, I don't want to even show these fields to the user while creation. I created another form for this:
class AddRootFolderForm(FolderForm):
class Meta:
model = Folder
exclude = ['parent', 'root', 'user']
As you can see, I exclude user aswell, this value will be set in the view. Currently, this is my view code:
#login_required
def create_rootfolder(request):
if request.method == 'POST':
form = FolderForm(data = request.POST,
user = request.user)
else:
form = AddRootFolderForm()
if form.is_valid():
new = form.save()
return redirect('show_rootfolder', root_id = new.id)
return render(request, 'create_rootfolder.html',
{ 'form': form })
This whole setup is working, but seems awful hackerish. Is there any better approach to hide certain fields from the user (meaning: Don't even show them as hidden fields), but include them in validation? My main problem is, that I can't use the same form for displaying and validating, because the excluded fields will not be validated, if I use AddRootFolderForm as single form instance.
I am aware that I can exclude the fields dynamically in the constructor, I even tried this, but it got my constructor bloated to 50 LOC, which seemed unclean.
So what would be the best approach to validate the model with all fields, even if they were not included in the form shown to the user?
Why validate fields, not used in Form?
The cleaning process is the way to check the data posted by a user. The rest of the data, required for Model operations must be added after the form validation
if form.is_valid():
new = form.save(commit=False)
new.user = request.user
...
new.save()

Categories