Save form as foreign key's object - python

There is 2 models with a ForeignKey Relationship, Profile model and Article model, I would like to save the connected user as profile.user when an Article object is created.
Here is the error I get with the code below :
'NoneType' object has no attribute 'user'
models.py :
class Profile(models.Model):
name = models.CharField(max_length=120)
user = models.OneToOneField(settings.AUTH_USER_MODEL, null=True, blank=True)
class Gig(models.Model):
profile = models.ForeignKey(Profile, null=True)
title = models.CharField(max_length=100, unique=True)
Here is my view for where I want to save profile.user
if request.method == 'POST':
form = GigForm(request.POST, request.FILES)
if form.is_valid():
save_it = form.save()
save_it.profile.user = request.user #error occured here.
save_it.save()
return redirect(reverse('own_gigs_details'))
else:
form = GigForm()
Forms.py
class GigForm(forms.ModelForm):
class Meta:
model = Gig
fields = ['title', 'image', 'body', 'price']
What am I doing wrong ?

When you first create a Gig by saving the form, there is no profile linked to it (because it wasn't declared in your form). So, your error occurs because save_it.profile has no value.
I think you are trying to link a Gig to the Profile of the user logged in. To do this, I think you can do
save_it.profile = request.user.profile
save_it.save()
instead of assigning the user itself.
OneToOneField lets you reference objects in both directions. So you can do user.profile and profile.user.

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, link a model field with an admin model field with ForeignKey

I'm trying to link a "normal" model field with an admin model field, for example I have a table "Post" and I want to add the admin username as a ForeignKey to the field "Author" of the table Post.
I mean :
class Post(models.Model):
title = models.CharField(max_length=50)
body = RichTextField(blank=True, null=True)
date = models.DateTimeField('date_posted')
username = models.ForeignKey(admin.username, on_delete=models.CASCADE)
Where admin.username refers the username of auth_user admin model
Thanks for your help
As the referencing the user model section of the documentation says, you can make use of settings.AUTH_USER_MODEL to obtain a reference to the user model that is used. You can use the to_field=… [Django-doc] to specify to what field of the model it should refer, so:
from django.conf import settings
class Post(models.Model):
title = models.CharField(max_length=50)
body = RichTextField(blank=True, null=True)
date = models.DateTimeField('date_posted')
author = models.ForeignKey(
settings.AUTH_USER_MODEL,
to_field='username'
on_delete=models.CASCADE,
editable=False
)
By specifying editable=False [Django-doc] it will not automatically show up in ModelForms.
In views, you can then set the logged in user as author by specifing the author attribute. For example:
from django.contrib.auth.decorators import login_required
from django.shortcuts import redirect
#login_required
def some_view(request):
if request.method == 'POST':
form = PostForm(request.POST)
if form.is_valid():
form.instance.author = request.user
form.save()
return redirect('name-of-some-view')
else:
form = PostForm()
return render(request, 'some_template.html', {'form': form})
Note: A ForeignKey does not store the string representation (or name) of the
referenced object in the column, it stores the primary key of the record it
references in a column with an _id suffix to a ForeignKey field. Therefore
ForeignKeys usually do not end with a _name suffix. You might want to
consider renaming the username field to author.
How about something like this?
class Post(models.Model):
title = models.CharField(max_length=50)
body = RichTextField(blank=True, null=True)
date = models.DateTimeField('date_posted')
user = models.ForeignKey(auth_user, on_delete=models.CASCADE)
#property
def username(self): return self.user.username
Usage:
some_post = Post.objects.get(id='the_post_id')
print(some_post.username) # prints some_post.user.username

Can't update user profile in my Django app Django

I have created a Edit Profile form in my Django app but it doesn't save in the database.
This is the profile model:
class UserProfile(models.Model):
user = models.OneToOneField(User, related_name='profile', primary_key=True) #Each User is related to only one User Profile
city_search_text = models.CharField(blank=True, max_length=300)#user picks a city from autocomplete and in the view I get or create a City object
city = models.ForeignKey(City, blank=True, null=True, related_name='city') #Each User Profile must be related to one city.
prof_pic = models.ImageField(blank=True, upload_to='profile_pictures')
dob = models.DateField(blank=True, null=True)
def __str__(self):
return self.user.first_name
This is the form:
class EditProfileForm(forms.ModelForm):
class Meta:
model = UserProfile
fields = ('dob',)#I'm testing to update this field only
def save(self, commit=True):
profile = super(EditProfileForm, self).save(commit=False)
if commit:
profile.save()
return profile
This is the view:
def editprofile(request):
if request.method == 'POST':
edit_profile_form = EditProfileForm(request.POST, instance=request.user)
if edit_profile_form.is_valid():
profile = edit_profile_form.save(commit=False)
profile.save()
if 'next' in request.GET:
return redirect(request.GET['next'])
else:
print (profile_form.errors)
else:
edit_profile_form = EditProfileForm(instance=request.user.profile)
return render(request, 'excurj/editprofile.html', {'edit_profile_form':edit_profile_form,})
After I submit the form it forwards me to index page okay but the values remain the same in the user's profile.
Seems like
if edit_profile_form.is_valid():
isn't getting called at all, and your data is not saved. That means your form has invalid data, and you should check for form errors to detect those.
Also, you are trying to print form errors if the request isn't POST, which makes no sense and won't help you printing form errors. Try using this way;
if edit_profile_form.is_valid():
profile = edit_profile_form.save(commit=False)
profile.save()
else:
print (profile_form.errors)
And check your form for errors.
I figured it out eventually. In the view I should have passed an instance of the profile not the User object. So it needs to be like this:
edit_profile_form = EditProfileForm(request.POST, instance=request.user.*profile*)

Changing the Userprofile

Hello i have UserSettings and the user as the possibility to use a ProfilePicture. The Problem is the User cannot change any of his profile Settings. since i get the Error Unique Constraint failed or (thats what i get right now) 'int' object has no attribute '_committed'
I think the problem is in the view but i dont know how to change it.
My model:
class UserSettings(models.Model):
profileimage = models.ImageField(verbose_name=_(u"Change Your Profilepicture"),upload_to=upload_location,
default=1,
blank=True,
)
Info = models.CharField(verbose_name=_(u"Tell us about yourself"),max_length=500,default="I'm a Human")
status =
City = models.CharField(verbose_name=_(u"Where are you from"),max_length=500,default='Earth')
user = models.OneToOneField(User, default=1)
objects = models.Manager()
class Meta:
ordering =['-user']
def __unicode__(self):
return self.user.username
My View:
#login_required
def userprofiletwo(request):
user = request.user
form = UserSettingsForm(request.POST or None, request.FILES or None)
if form.is_valid():
user = request.user
form.save()
messages.success(request, 'Your personal Settings are Updated')
return redirect('userprofiletwo')
context = {
'form':form,
}
return render(request, 'userprofile/userprofiletwo.html', context)
I tried to change the OnetoOne field on the model to FeoreignKey or ManytoMany but this made new userprofiles instead of replacing the old information with new information.
Thanks in advise.
You need to pass in the current profile into the form.
form = UserSettingsForm(request.POST or None, request.FILES or None, instance=request.user.profile)

Django : Filter, limit and display file filed dynamicly with generic inlineformset

I need your help, because I'm stuck in a project with no idea about how to continue.
I need to implement a simple "generic" document management app to integrate it in my project. I can have many kind of documents that are defined by "utilisation_type" in "DocumentType" model.
At the end, I need to display a page with an inline form, with only one of each "DocumentType" with (in this case) "utilisation_type"= "USER".
Like this, the user will get, for exemple, a page with 3 buttons, each button corresponding to only one DocumentType with "utilisation_type"= "USER" and he will be able to upload only one of each requested document.
Here are my models :
from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType
UTILISATION_TYPE_CHOICES = (
("USER", _(u"User")),
("PROJECT", _(u"Project")),
("TEAM", _(u"Team")),
("ALL", _(u"All")),
)
class DocumentType(models.Model):
document_type = models.CharField(max_length=40, choices=DOCUMENT_TYPE_CHOICES)
title = models.CharField(_(u'Titre'), max_length=255, blank=False, null=True)
utilisation_type = models.CharField(max_length=15, choices=UTILISATION_TYPE_CHOICES)
class Document(models.Model):
document_type = models.ForeignKey(DocumentType)
content_type = models.ForeignKey(ContentType)
object_id = models.PositiveIntegerField()
content_object = GenericForeignKey('content_type', 'object_id')
document = models.FileField(_(u"Document"), upload_to='upload/documents', null=True, blank=True)
class MyUserManager(BaseUserManager):
first_name = models.CharField(_(u'Firstname'), max_length=90, blank=False)
last_name = models.CharField(_(u'Lastname'), max_length=90, blank=False)
Here is my view :
#login_required
def documents(request):
"""
User's documents edition
"""
user = request.user
DocumentInlineFormSet = generic_inlineformset_factory(Document)
if request.method == 'POST':
formset = DocumentInlineFormSet(request.POST, request.FILES, instance=user)
if formset.is_valid():
formset.save()
messages.success(request, _(u'Profil updated'), fail_silently=True)
return redirect('users.views.documents')
else:
formset = DocumentInlineFormSet(instance=user)
return render_to_response('users/user_documents.html', {'formEditDocumentFormset': formset,}, context_instance=RequestContext(request))
I hope I've been clear enough in my explanation. Do not hesitate to ask more details if needed.
I think the most simple way is to check the type of every single document on it's saving. if you allow to save each doctype only once you will get only different doctypes for each user. In that case you do not need to use double filtering or whatever.
You can rewrite the save() method for each form in a formset by creating your CustomInlineFormSet. Here is the link: https://docs.djangoproject.com/en/1.8/topics/forms/modelforms/#overriding-methods-on-an-inlineformset.
It could be smth like this for example:
from django.forms.models import BaseInlineFormSet
class DocumentInlineFormSet(BaseInlineFormSet):
def clean(self):
super(DocumentInlineFormSet, self).clean()
for form in self.forms:
document = form.save(commit=False)
#check your document here
document.save()

Categories