Two models in django - python

I am learning Django so I don´t know about this.
What is happening is that I have two tables.
Table BlogPost : Save all post.
Table Categoria : Save the ID of the category of register post.
My model.py
class BlogPost(models.Model):
title=models.CharField(max_length=150)
author = models.ForeignKey(User)
categorias_post = models.ManyToManyField(Categoria)
body = RichTextField(('Content of post'))
creada_en = models.DateTimeField(auto_now_add=True)
actualizada_al = models.DateTimeField(auto_now=True)
My forms.py
class FormularioPost(forms.ModelForm):
class Meta:
model = BlogPost
fields = ('title', 'author', 'categorias_post', 'body')
My views.py
def postregistrado(request):
if request.method == "POST":
form = FormularioPost(request.POST)
if form.is_valid():
post = form.save(commit=False)
post.author = request.user
post.save
messages.success(request, 'Su post ha sido registrado con éxito.')
else:
form = FormularioPost()
return render_to_response(
"postRegistrado.html",
locals(),
context_instance=RequestContext(request),
)
I want to insert in two different tables from the same views.py. Can anyone help me with that?

When you are using commit=False, you have to explicitly call save_m2m() to save the many to many fields.
if form.is_valid():
post = form.save(commit=False)
post.author = request.user
post.save() #Note that it is a function call.
post.save_m2m()
You can read more on this in the documentation here
Another side effect of using commit=False is seen when your model has
a many-to-many relation with another model. If your model has a
many-to-many relation and you specify commit=False when you save a
form, Django cannot immediately save the form data for the
many-to-many relation. This is because it isn’t possible to save
many-to-many data for an instance until the instance exists in the
database.
To work around this problem, every time you save a form using
commit=False, Django adds a save_m2m() method to your ModelForm
subclass. After you’ve manually saved the instance produced by the
form, you can invoke save_m2m() to save the many-to-many form data.
Another thing is, make sure you add the login_required decorator on this view, so that you don't run into weird issues when post.author = request.user evaluates to anonymous users

Related

ManyToMany Relationship between two models in Django

I am trying to build a website that users can add the courses they are taking. I want to know how should I add the ManyToMany relationship. Such that we can get all users in a course based on the course code or instructor or any field. And we can also get the courses user is enrolled in. Currently, my Database structure is:
class Course(models.Model):
course_code = models.CharField(max_length=20)
course_university = models.CharField(max_length=100)
course_instructor = models.CharField(max_length=100)
course_year = models.IntegerField(('year'), validators=[MinValueValidator(1984), max_value_current_year])
def __str__(self):
return self.course_code
and my user model:
class Profile(AbstractUser):
bio = models.TextField()
image = models.ImageField(default='defaults/user/default_u_i.png',
courses = models.ManyToManyField('home.Course',related_name='courses')
def __str__(self):
return self.username
I was wondering should ManyToMany relationship be in User model or the course model? Or will it make any difference at all?
EDIT: For adding course to post object now I am using this view but it seems to not work:
#login_required
def course_add(request):
if request.method == "POST":
form = CourseForm(request.POST or none)
if form.is_valid():
course = form.save()
request.user.add(course)
else:
form = CourseForm
context = {
'form':form
}
return render(request,'home/courses/course_add.html', context)
For a relational databases, the model where you define the ManyToManyField does not matter. Django will create an extra table with two ForeignKeys to the two models that are linked by the ManyToManyField.
The related managers that are added, etc. is all Django logic. Behind the curtains, it will query the table in the middle.
You however need to fix the related_name=… parameter [Django-doc]. The related_name specifies the name of the relation in reverse so from Course to Profile in this case. It thus should be something like 'profiles':
class Profile(AbstractUser):
bio = models.TextField()
image = models.ImageField(default='defaults/user/default_u_i.png',
courses = models.ManyToManyField('home.Course', related_name='profiles')
def __str__(self):
return self.username
You thus can obtain the people that particiate in a Course object with:
mycourse.profiles.all()
and you can access the courses in which a Profile is enrolled with:
myprofile.courses.all()
For more information, see the Many-to-many relationships section of the documentation.
You can add a course to the courses of a user with:
#login_required
def course_add(request):
if request.method == 'POST':
form = CourseForm(request.POST)
if form.is_valid():
course = form.save()
request.user.courses.add(course)
else:
form = CourseForm()
context = {
'form': form
}
return render(request,'home/courses/course_add.html', context)
You don't need to add the related name. Default is "courses_set" in your case.
Here is excerpt from: https://docs.djangoproject.com/en/dev/topics/db/queries/#backwards-related-objects
Following relationships “backward” If a model has a ForeignKey,
instances of the foreign-key model will have access to a Manager that
returns all instances of the first model. By default, this Manager is
named FOO_set, where FOO is the source model name, lowercased. This
Manager returns QuerySets, which can be filtered and manipulated as
described in the “Retrieving objects” section above.

How do I `save()` a Profile model if it has a OneToOne relationship to my AbstractBaseUser model without running into UNIQUE constraint errors?

I have two separate models:
1. A MyUser model which inherits from AbstractBaseUser, and has a field of profile_page = models.OneToOneField(Profile, null=True)
2. A Profile model with a user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, null=True, blank=True) relationship to the User model.
I am attempting to allow users of the site to edit their own Profile information by providing them a ProfileForm ModelForm.
In my user_profile/views.py I have this profile_edit FBV:
def profile_edit(request):
if request.method == 'POST':
form = ProfileForm(request.POST)
if form.is_valid():
form.instance.user = request.user
form.instance.save()
return redirect('/profile/edit')
else:
form = ProfileForm(instance=request.user)
print(request.user)
print('hello get req here')
context = {
'form': form,
}
return render(request, 'profile_edit.html', context)
When I attempt to update profile information in profile_edit.html, the POST data will go through the first time, but not get saved to the DB. On a second attempt, I receive a UNIQUE constraint failed: user_profile_profile.user_id error.
form.instance.save() is pointed to as the direct cause of the exception.
In my estimation the error has something to do with the fact that upon creation of a new user, an initial unique ID is created for the user. So when I try to save() the Profile object, I think it is attempting to save() a new User, thereby causing the Unique Constraint failure.
How can I configure the two models in such a way that upon creation of a new User, the User has the ability to update their own Profile information? What should change in my views.py?
You're doing two things wrong, both to do with the instance argument.
Firstly, in the GET block, you're passing request.user as the instance. But this is a Profile form; you need to pass the user profile, not the user itself:
form = ProfileForm(instance=request.user.profile)
Secondly, in the POST block, you're not passing an instance at all. This means that Django won't know to update an existing item, but will try and create a new one. Again you need to pass the profile:
form = ProfileForm(request.POST, instance=request.user.profile)
Note also though, you should consider whether you need a separate Profile model at all. Initially that was the recommended way to provide custom user information, but for several versions Django has given you a way to use a customised User model - which you are in fact doing. You probably want to put the profile data directly in MyUser, rather than in a separate model with a one-to-one relationship.
Edit after comment It sounds like you're not automatically creating a profile when you create the user. You could do something like this in the view:
def profile_edit(request):
try:
profile = request.user.profile
except Profile.DoesNotExist:
profile = Profile(user=request.user)
if request.method == 'POST':
form = ProfileForm(request.POST, instance=profile)
...
else:
form = ProfileForm(instance=profile)

Django: saving dynamic forms with foreign key

I would like to seek assistance and guidance to my problem.
I have the following models:
class myinfo(models.Model):
name = models.CharField(max_length=30, null=True)
class mynumbers(models.Model):
fkey = models.ForeignKey("myinfo")
Job_Position = models.CharField(max_length=30, null=True)
The mynumbers model is dynamically generated via django-dynamic-formset.
My form
class info(ModelForm):
name= forms.CharField( max_length=20)
class Meta:
model = APPLICANT_DATA
fields = ('name',)
class numbers(ModelForm):
number = forms.CharField( max_length=20)
class Meta:
model = APPLICANT_DATA
fields = ('number',)
If you want to save your dynamic form fields you have to do this in views
for field in formset:
field.save()
My views:
def index(request):
aformset = formset_factory(numbers)
formset = aformset(request.POST)
form = info(request.POST)
if request.method == 'POST':
if form.is_valid():
if formset.is_valid():
for field in formset:
formset.save()
form.save()
But the problem starts when my dynamically generated field has a foreign key(mynumbers) which raises an error must be a myinfo instance. How would I save the 2 forms where mynumbers has a foriegn key to myinfo? Is there a better way to what I did? Thank you in advance,
This is where inlineformset_factory would be used. This allows you to have a parent model and a number of child models (related w/ parent via a foreignkey) and save them together. There are many arguments that can be passed to the inlineformset_factory in order to customize behavior (such as the minimum and maximum number of inline forms allowed, whether the user can delete inline forms, etc.) but the crux is shown below.
# views.py
from django.forms.models import inlineformset_factory
from my_app.forms import numbers as NumberForm
from my_app.forms import info as InfoForm
from my_app import models
myFormset = inlineformset_factory(models.myinfo,
models.mynumbers,
form=NumberForm
)
def index(request):
if request.POST:
form = InfoForm(request.POST)
if form.is_valid():
info = form.save(commit=False)
formset = myFormset(request.POST, instance=info)
if formset.is_valid():
info.save()
formset.save()
return HttpResponse('saved successfully')
else:
form = InfoForm()
formset = myFormset(instance=models.myinfo())
return render_to_response("recipes/submit.html", {
"form": form,
"formset":formset,
},
context_instance=RequestContext(request))
Please note: In your question, you typed for field in formset: formset.save(). A formset is a collection of forms, not a collection of fields.
Formsets can be tricky and require the template rendered correctly with additional template components that are not part of regular forms (such as the management_form variable that allows for Django to properly process what has been added/deleted/moved/changed). It's definitely worth doing some tutorials in order to get an idea of best practices so that you don't go down a troubleshooting rabbithole w/ your custom implementation.
I suggest this post from Charles Leifer as a good entry to get acquainted with the basics.

Django NOT NULL constraint failed userprofile.user_id in case of uploading a file

I am trying to practice a simple project: A user registers (using Django registration-redux), uploads some file, and then s/he is provided with a list of her files, being downloadable. Here are my models.py, forms.py, and views respectively:
models.py
class UserProfile(models.Model):
user = models.ForeignKey(User, related_name='uploaded_by')
names = models.CharField(max_length=40)
lastname = models.CharField(max_length=50)
email = models.EmailField()
uploads = models.FileField(upload_to= 'blablabla')
def __str__(self):
return self.email
forms.py
class UserProfileForm(forms.ModelForm):
class Meta:
model = UserProfile
fields = ['names', 'uploads']
view.py
from .forms import UserProfileForm
from .models import UserProfile
#login_required()
def new(request):
form = UserProfileForm(request.POST or None, request.FILES or None)
if form.is_valid():
form.save()
context = {'title': 'welcome', 'form': form}
return render(requst, 'upform.html', context)
however when I login with a user and try to upload a file I get the error: IntegrityError at /new
NOT NULL constraint failed: _userprofile.user_id
After digging a lot I noticed someone suggested the reason of the error is because the user is not included anyhow in the process of posting the form, so I tried whatever came to my mind and the case in which I added the user field to the forms.py worked:
forms.py
class UserProfileForm(forms.ModelForm):
class Meta:
model = UserProfile
fields = ['names', 'uploads']
the problem however is that the form shown in the browser now includes a drop-down list containing all the registered users. I tried to associate the logged-in user with the form in the views by I kept seeing different errors.
My question is: How can I associate the uploads with the logged-in user in a transparent manner.
sorry if the question is too newbie-liked
Keep the user out of the form and add it on save:
if form.is_valid():
profile = form.save(commit=False)
profile.user = request.user
profile.save()
I must say your model looks a bit odd; you have multiple profiles for each user, each with a single upload. Seems more likely you want a single profile, with a OneToOne relationship to User, than a separate Uploads model with a ForeignKey to UserProfile.
I added the user field to the forms.py worked:
This probably also opens up a security hole, because you could then set the user from outside of your application, overriding the logged-in user.
After digging a lot I noticed someone suggested the reason for the error is because the user is not included anyhow in the process of posting the form.
You figured that quite right. If the user is not a field, the form never knows how to fill the user field of UserProfile. Since you cannot save a UserProfile without a filled in user foreign key, you get the error.
There are various ways around this:
One way to solve the problem is to use save(commit=False) on the form, patch the user into the unsaved instance and manually saving the object to the database:
if form.is_valid():
profile = form.save(commit=False)
profile.user = request.user
profile.save()
This slightly violates the encapsulation because you now handle the database save outside of the form.
You are also able to provide a initial "template" instance to the form:
form = UserProfileForm(
request.POST,
request.FILES,
instance=UserProfile(user=self.request.user)
)
You probably want to do so, anyway because the form also allows to edit an existing userprofile. Currently you are saving a new UserProfile each time and since user is not unique=True in your model class, you will get multiple profiles per user.
If you do not want this to be possible, check Daniel Roseman's answer, because then you probably want UserProfile.user to be a OneToOne field.
In this case, we can simplify to
profile, created = UserProfile.objects.get_or_create(user=request.user)
form = UserProfileForm(
request.POST,
request.FILES,
instance=profile
)
Note also that I removed the or None as it is not necessary. BaseForm (which ModelForm derives from) does this check for you (it actually does self.data = data or {}) which essentially undoes the prior none-ification)
You can do following the advice of Daniel.
class UserProfile(models.Model):
user = models.OneToOneField(User, related_name='uploaded_by')
names = models.CharField(max_length=40)
lastname = models.CharField(max_length=50)
email = models.EmailField()
class UserFiles(models.Model):
user = models.ForeignKey(UserProfile)
file = models.FileField(upload_to= 'blablabla')
Remember OneToOneField is similar a ForeignKey with the attribute unique=True.

Django ModelForm ManyToManyField isn't able to update selected values

I am building my first project in Django 1.8 with python 3.4. I have the following model called Lid in models.py:
class Lid(models.Model):
...
vereniging = models.ManyToManyField(Vereniging, blank=True)
I use the following ModelForm, forms.py
class LidForm(forms.ModelForm):
class Meta:
model = Lid
exclude = []
When I use this ModelForm to create a form to make a new object, a multiple select box appears and I am able to select multipe Vereniging objects. This is my view in views.py:
def add_lid(request):
if request.method == 'POST':
form = LidForm(request.POST, request.FILES)
if form.is_valid():
form.save()
messages.success(request, 'Succes.')
return HttpResponseRedirect(reverse('leden:home'))
else:
form = LidForm()
return render(request, 'leden/lid/addlid.html', {'formset': form})
When I want to edit my objects however, I am not able to change the selected selected Vereniging objects.
def edit_lid(request, lid_id):
lid = get_object_or_404(Lid, pk=lid_id)
if request.method == 'POST':
form = LidForm(request.POST, request.FILES, instance=lid)
if form.is_valid():
nieuwlid = form.save(commit=False)
nieuwlid.inschrijving_oras = lid.inschrijving_oras
nieuwlid.save()
messages.success(request, 'Success.')
return HttpResponseRedirect(reverse('leden:lid', kwargs={'lid_id': lid_id}))
else:
form = LidForm(instance=lid)
return render(request, 'leden/lid/editlid.html', {'formset': form, 'lid': lid})
So this is basically my problem: when using a ModelForm, I am only able to set ManyToMany relationships when creating an object. I am not able to update these m2m relationships. Do you know what I am doing wrong?
Use save_m2m(). From the docs:
Another side effect of using commit=False is seen when your model has a many-to-many relation with another model. If your model has a many-to-many relation and you specify commit=False when you save a form, Django cannot immediately save the form data for the many-to-many relation. This is because it isn’t possible to save many-to-many data for an instance until the instance exists in the database.
To work around this problem, every time you save a form using commit=False, Django adds a save_m2m() method to your ModelForm subclass. After you’ve manually saved the instance produced by the form, you can invoke save_m2m() to save the many-to-many form data.

Categories