django newbie. Having trouble with ModelForm - python

i'm trying to write a very simple django app. I cant get it to show my form inside my template.
<form ...>
{{form.as_p}}
</form>
it shows absolutely nothing. If I add a submit button, it only shows that.
Do I have to declare a form object that inherits from forms.Form ? Cant it be done with ModelForms?
[UPDATE]Solved! (apologize for wasting your time)
In my urls file I had:
(r'login/$',direct_to_template, {'template':'register.html'}
Switched to:
(r'login/$','portal.views.register')
And yes, I feel terrible.
Background:
I have a Student model, and I have a registration page. When it is accessed, it should display a textfield asking for students name. If the student completes that field, then it saves it.
#models.py
class Student(models.Model):
name = models.CharField(max_length =50)
#forms.py
class StudentForm (forms.ModelForm):
class Meta:
model = Student
So, here is my view:
def register(request):
if request.method == 'POST':
form = StudentForm(request.POST)
if form.is_valid():
form.save()
return render_to_response('/thanks/')
else:
student = Student()
form = StudentForm(instance =student)
return render_to_response('register.html',{'form':form})

The problem is in your view. You will have no existing student object to retrieve from the database. The following code sample will help you implement an "create" view.
As a side note, you might like using the direct_to_template generic view function to make your life a bit easier.
def add_student(request):
if request.method == 'POST':
form = StudentForm(request.POST)
if form.is_valid():
new_student = form.save()
return HttpResponseRedirect('/back/to/somewhere/on/success/')
else:
form = StudentForm()
return direct_to_template(request,
'register.html',
{'form':form})

Related

Cannot add model instance to OneToManyField in django - "None has no attribute add"

I already have seen this bug in other post, but still in trouble.
I'm trying to create a social network like instagram where users will be able to publish posts (photos).
I have User class which herit from AbstractUser, and got a OneToMany field of posts: each user can publish many posts.
After successfully pulling my photo from: PostForm(request.POST, request.FILES) and saving it correctly, I cannot add this photo to the current user's publications/posts and got error:
'NoneType' object has no attribute 'add'
def blog_and_photo_upload(request):
form = PostForm()
if request.method == 'POST':
form = PostForm(request.POST, request.FILES)
if form.is_valid():
user = get_user(request) # user instance is correct with good pk
post = Post.objects.create(image=form.cleaned_data['image']) # post instance looks correct also
post.save()
user.save()
user.posts.add(post) # row doesnt work
redirect('home')
return render(request, 'base/upload_post.html', {'form': form})
models.py
class Post(models.Model):
...
image = ResizedImageField(size=[300, 300], blank=True, upload_to='posts')
class User(AbstractUser):
...
posts = models.ForeignKey(Post, on_delete=models.Cascade, null=True)
You can simply update the form like this:
post = Post.objects.create(image=form.cleaned_data['image']) # post instance looks correct also
post.save()
user.posts = post
user.save()
return redirect('home')
But, I think the design of the model is wrong, User to Post relation should be like this:
Class User(...):
posts = models.ManyToManyField(Post)
In that way, your original implementation should work. (Probably you don't need user.save() call in your view).
At first there should be return redirect(...) not only redirect() and secondly try to use the following view:
def blog_and_photo_upload(request):
form = PostForm()
if request.method == 'POST':
form = PostForm(request.POST, request.FILES)
if form.is_valid():
user = get_user(request) # user instance is correct with good pk
post = Post.objects.create(image=form.cleaned_data['image']) # post instance looks correct also
post.save()
user.posts.add(post) # add post to user's posts field
user.save()
return redirect('home')
return render(request, 'base/upload_post.html', {'form': form})
You need to bind first Post with User model like add a ForeignKey or a ManyToManyFields to relate them
posts = models.ForeignKey(User)
then you will be able to call it like you did
user.posts # this won't return None
Check this many to many field docs: https://docs.djangoproject.com/en/4.1/topics/db/examples/many_to_many/

How to bind a django form to an object instance

I come from DRF background so please already assume that I might be getting something wildly wrong here. I am trying to use Django Form as a sort of proxy for DRF serializers. As in, when I fetch an object, I can quickly render it back, and I can of course accept POST requests and store them. What I can't seem to find is how do I use my object instances to process them with forms.
Here's my form:
class ProfileForm(ModelForm):
class Meta:
model = UserProfile
fields = ('name', 'profile_pic')
The actual model:
class UserProfile(models.Model):
user = models.OneToOneField(CustomUser, null=True, on_delete=models.CASCADE)
name = models.CharField(max_length=200, null=True)
profile_pic = models.ImageField(upload_to='profile_image', null=True, blank=True)
def __str__(self):
return str(self.user)
My view:
def get_profile(request):
if request.user.is_authenticated:
if request.method == 'POST':
form = ProfileForm(request.POST)
if form.is_valid():
# update the model
profile = request.user.userprofile
form = ProfileForm(model_to_dict(profile))
if not form.is_valid():
form = ProfileForm()
return render(request, 'profile-edit.html', {'form':form})
else:
return render(request, 'index.html')
So, in my view (which is a GET view), I'm doing something like this:
from django.forms import model_to_dict
profile = request.user.userprofile
form = ProfileForm(model_to_dict(profile))
return render(..., form)
Is this even the right approach? Besides, the problem is that my form doesn't seem to handle the profile_pic field properly (i.e. the img src field in the from html is just __).
Ideally I would want to build the form from the object instance itself, but that also doesn't work.
form = ProfileForm(instance = profile) is bound, but not valid
form = ProfileForm(UserProfile.objects.all()[0]) is bound, and I can access data items through form.data.<fields>, but as soon as do form.is_valid(), I get:
AttributeError: 'UserProfile' object has no attribute 'get'
UPDATE: form = ProfileForm(request.<GET/POST>, instance=profile) is actually bound but not valid with the error that field name is required, even though profile.name is indeed valid CharField.
So how can I take my object, then bind it to a form, and then return it to be rendered properly. Imgine this is a user profile that I render, but also let the user update it whenever they want. What is the Django way of doing it? (I come from DRF background.)
Most of the SO answers that I have found almost always initialize the form with request.POST which I don't need.
EDIT: My use case, I have an endpoint /profile which, when requested as GET, should return a profile (with all the info that their UserProfile object holds but the user can also edit any of the fields that they want to. Say they have edited a field, they can simply hit update, which would then submit the form. On the backend, there profile would get updated, and they would get served the latest profile details. Of course, I can do it by rendering a from using a template, but I instead wanted to use the form which is already there. As I mentioned model_to_dict approach kind of serves this point to some extent, except it breaks for images... so I thought there might be a solution.
Looking at your view you make some mistakes which simply make using the forms difficult. Firstly if you want to update a form you should instantiate the form with the model instance so instead of form = ProfileForm(model_to_dict(profile)) it should be form = ProfileForm(instance=profile).
Also right after this line you write:
if not form.is_valid():
form = ProfileForm()
Why check is_valid on a form that is not bound?
Your view should ideally look something like:
from django.contrib.auth.decorators import login_required
from django.shortcuts import redirect
#login_required
def get_profile(request):
profile = request.user.userprofile
form = ProfileForm(instance=profile)
if request.method == 'POST':
form = ProfileForm(request.POST, instance=profile)
if form.is_valid():
form.save()
return redirect('some-view-name')
return render(request, 'profile-edit.html', {'form':form})

Custom django form that has fields populated from database

I am new to Django. I have a custom form that uses forms.Modelform to create a custom form from my model. I have some data in my database already that I manually input for testing.
However, the user and course field shows up as dropdowns. But they do not have any data in the dropdown list. How can I have django to pull data from the database and display the information into each dropdown on my form?
models.py:
class Student(models.Model):
user = models.OneToOneField(User)
course = models.ForeignKey(Course)
view.py:
def home(request):
if request.method == 'GET':
form = StudentForm()
else:
form = StudentForm(request.POST)
if form.is_valid():
pass
return render(request, "request.html", {'form': form}, context_instance=RequestContext(request))
forms.py:
class StudentForm(forms.ModelForm):
class Meta:
model = Student
Update
Actually found out that the changes weren't saved to my DB. They are now loading into the form. However in the dropdown list, it is showing up as "Student Object", and "Course Object"
How can I make it so they show up with proper names?
I would advocate that you move away from doing this if this is testing, and instead follow the guidelines for testing as outlined in the django tutorials, i.e. it creates a fake database for you and you create Users and Courses via Users.objects.create(username=...)

Django 1.6: How to print the number of objects a user uploaded?

I'm looking to get the number of objects a user uploaded into the database. For example:
Let's say i have a model called "Link" (which adds a url with its title and description) into my models.py:
class Link(models.Model):
user = models.ForeignKey(User)
title = models.CharField(max_length=200)
link = models.URLField(max_length=200)
description = models.TextField()
With its form:
class UrlForm(ModelForm):
class Meta:
model = Link
And its view:
def linkurl(request):
form = UrlForm()
if request.method == 'POST':
form = UrlForm(data=request.POST, instance=Link())
if form.is_valid():
form.instance.user = request.user
form.save()
return redirect("home")
else:
form = UrlForm()
return render(request, "addurl.html", {'form': form})
So, knowing that the user can add urls to my app, i'm looking to print the number of urls the user uploaded into my app. Is that actually possible?
Thank you, in advance.
Use filter and count:
# change request.user to user id or user instance you need
user_links_count = Link.objects.filter(user=request.user).count()
There are multiple ways to achieve this.
Use a query and pass it into your template:
ndpu has already pointed it out here:
https://stackoverflow.com/a/22288960/659900
user_links_count = Link.objects.filter(user=request.user).count()
render(request, "addurl.html", {'form': form, linkscount: user_links_count})
Use a model property which you can use to access your model over the form directly, without touching the render method in your view.
add the following method to your link model: (warning: untested!)
#property
def user_links_count(self):
try:
return self.objects.filter(user=self.user).count()
except:
return 0 #or any error message you want
now in your template, you can access your link model directly via the form you are using. assuming you are not using an empty model:
{{ form._meta.model.user_links_count }}
However I would recommend ndpu's solution for a one off solution. Consider an enhancement to your model if you need this functionality more often

Django modelForm changes aspect when is submitted

I'm trying to create a web using django where a form is prompted to the user, that can fill some values of it and submit them. Then, the python program will fill the rest and show the form filled by the user, with also the fields filled by the server. I am using modelForms, as I want a complete matching between my model and my form.
For several reasons I have come to do it using the following code, but I don't know why after submitting the form, the fields don't appear as CharFields anymore, but as something similar to 'html labels', and are not editable anymore.
The code is a very simplified version of mine, where there is only one field, filled by the user.
views.py:
def manage_printers(request):
p = PrinterForm(request.POST or None)
if request.method == 'POST':
if p.is_valid():
f = p.save(commit=False)
f.name = request.POST.get('name')
f.save()
return render_to_response('web.html', {'printer': f})
else:
return HttpResponse("form not valid")
return render_to_response('web.html', {'printer': p}, )
models.py:
class Printer(models.Model):
name = models.CharField(max_length=200, default=' ')
def __unicode__(self):
return self.name
class PrinterForm(ModelForm):
class Meta:
model = Printer
web.html:
<form action="/useriface/" method="post">
<p>Name: {{ printer.name }}</p>
</form>
As I explained in the comment, when you save a form, you get a model instance. A model instance is not a form. It doesn't have form fields. When you pass the saved instance into the template in place of a form, you won't display form fields, you'll display the values of the saved instance's fields. If you want to redisplay the form, you should pass that into the template, not the saved instance.

Categories

Resources