I need to set the user that is creating the post in the add view:
#login_required
def add(request):
if request.method == 'POST':
form = BusinessForm(request.POST)
form.user_id = request.user.id
if form.is_valid():
form.save()
return HttpResponseRedirect(reverse('listing.views.detail'), args=(f.id))
else:
form = BusinessForm()
return render_to_response('business/add.html', {'form':form},
context_instance=RequestContext(request))
I assign the user id form.user_id = request.user.id but when saving, it still gives me an error Column user_id cannot be null
Am I doing something wrong? Thanks
EDIT:
I am excluding the user from the form in the model:
class BusinessForm(ModelForm):
class Meta:
model = Business
exclude = ('user',)
Could that be causing the problem?? How can I work around this?
EDIT 2:
Edited my BusinessForm() class as suggested but did not work:
class BusinessForm(ModelForm):
class Meta:
model = Business
exclude = ('user',)
def __init__(self, *args, **kwargs):
self.request = kwargs.pop('request', None)
return super(BusinessForm, self).__init__(*args, **kwargs)
def save(self, *args, **kwargs):
kwargs['commit']=False
obj = super(BusinessForm, self).save(*args, **kwargs)
if self.request:
obj.user = self.request.user
obj.save()
Business model
class Business(models.Model):
name = models.CharField(max_length=200)
user = models.ForeignKey(User, unique=False)
description = models.TextField()
category = models.ForeignKey(Category)
address = models.CharField(max_length=200)
phone_number = models.CharField(max_length=10)
website = models.URLField()
image = models.ImageField(upload_to='business_pictures',blank=True)
You don't have to use init or save overrides for this.
You're just setting an attribute on your form and the form doesn't do anything with it. It doesn't magically behave like a model instance (your form wouldn't have a user_id attribute).
Since your form is a ModelForm, you can simply call save on it with commit=False to get the unsaved instance, set the user, then call save on the instance.
if request.method == 'POST':
form = BusinessForm(request.POST)
if form.is_valid():
business = form.save(commit=False)
business.user = request.user
business.save()
this seems to be exactly what your looking for.
Related
I have a model (Letter) with a foreign key, pointing to another model (Company) with a foreign key. Below is a simple schema from models.py
from django.contrib.auth.models import User
class Company (models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE, null=True, editable=False)
date_created = models.DateField(default=timezone.now, null=True)
company_name = models.CharField(max_length=100, null=True)
class Letter(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='letterhead_user', null=True)
company = models.ForeignKey(Company, related_name = "company_letter", on_delete=models.CASCADE, null=True)
subject = models.CharField(max_length=5000, null=True)
body = models.TextField()
I have created a form where users can create Letters with the model through ModelForm.
class LetterForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(LetterForm, self).__init__(*args, **kwargs)
self.fields['company'].widget.attrs = {'class': 'input',}
self.fields['subject'].widget.attrs = {'class': 'input', 'placeholder': 'RE: ...'}
self.fields['body'].widget.attrs = {'class': 'textarea',}
class Meta:
model = Letter
fields = ('company', 'subject', 'body',)
The View:
def letter_form (request):
form = LetterForm()
if request.method == 'POST':
form = LetterForm(request.POST, request.FILES)
form.instance.user = request.user
if form.is_valid():
form.save()
return redirect('letter')
Currently, when the user is presented with a form to create a Letter, on the Company field, all the companies from all the users appear. See the pic below:
Front end Form
I would like only the companies that the User has created to appear in the drop-down, not all companies from all users. Or to be able to select the first company that the User has created.
You can specify the logged in user in the form and filter accordingly. In the constructor of the form we thus limit the queryset with:
class LetterForm(forms.ModelForm):
def __init__(self, *args, user=None, **kwargs):
super().__init__(*args, **kwargs)
self.fields['company'].widget.attrs = {'class': 'input',}
self.fields['subject'].widget.attrs = {'class': 'input', 'placeholder': 'RE: …'}
self.fields['body'].widget.attrs = {'class': 'textarea',}
if user is not None:
self.fields['company'].queryset = Company.objects.filter(user=user)
# …
and in the view, we then pass the logged in user to the constructor of the form:
from django.contrib.auth.decorators import login_required
#login_required
def letter_form(request):
if request.method == 'POST':
form = LetterForm(request.POST, request.FILES, user=request.user)
form.instance.user = request.user
if form.is_valid():
form.save()
return redirect('letter')
else:
form = LetterForm(user=request.user)
# …
Note: You can limit views to a view to authenticated users with the
#login_required decorator [Django-doc].
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.
If you use a ModelForm for Letterit may look like this:
class LetterForm(forms.ModelForm):
class Meta:
model = Letter
fields = ["company", "subject", "body"]
# you need to init the form with the right user instance
def __init__(self, user=None, *args, **kwargs):
# call the default __init__ behavior
super().__init__(*args, **kwargs)
# this is the trick, you will filter the companies queryset here
if user:
self.fields['company'].queryset = Company.objects.filter(user=user)
So you need to pass down the user in the form from your view:
something like:
def my_view(request):
# assuming the user follow the standard Django user implementation
# and you user is logged in
form = LetterForm(request.POST or None, user=request.user)
if form.is_valid():
form.save()
# then redirect the user to whatever success page
# render the form
return render(request, "your_template.html", {"form": form})
As a newbie in Django, I'm sure there is something obvious I'm not seeing. I have a user model with a one to one relationship to a userprofile model, where I'm storing the profile photo. I mixed DetailView and Formview because I want the user to go to his details page and update just the photo, but somehow its not working for me. I know I could do the job with UpdateView, but for didactic purposes, can anyone tell me why this is not working? I'm trying to updated the model fields in the form_valid method but this is not working, they just remain with the old values. I thought at the beginning it was the photo that could not be updated because of some errors on my side, but I've tried also updating other string fields and it doesnt work. Here the code: (the commented out fields are the places where I tried updating several model fields using get_object_or_404 and other functions)
class UserDetail(FormMixin, DetailView):
template_name = "users/user_detail.html"
model = User
form_class = forms.UserPhotoForm
def get_success_url(self):
return reverse('users:user_detail', args=[str(self.get_object().pk)])
def get_context_data(self, **kwargs):
user = self.get_object()
form = forms.UserPhotoForm(instance=user)
context = super().get_context_data(**kwargs)
context['user_rating'] = CotizacionReview.objects.filter(cotizacion__user=self.get_object()).aggregate(Avg('nota'))
context['form'] = form
return context
def form_valid(self, form):
form.save()
return super(UserDetail, self).form_valid(form)
def post(self, request, *args, **kwargs):
a = get_object_or_404(User, pk=self.get_object().id).userprofile
form = forms.UserPhotoForm(request.FILES['avatar'], instance=a)
# get_object_or_404(User, pk=self.get_object().id).apellido = '1234'
if form.is_valid():
# print(get_object_or_404(User, pk=self.get_object().id).userprofile.avatar)
# I tried updating several model fields here, but didnt work
# print(request.FILES['avatar'])
return self.form_valid(form)
else:
return self.form_invalid(form)
Here the model:
class UserProfile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
avatar = models.ImageField(upload_to='profile_pics', default='profile_pics/default-user-icon-4.jpg', blank=True)
telefono = models.CharField(max_length=12, blank=True)
nombre = models.CharField(max_length=64, blank=True)
apellido = models.CharField(max_length=64, blank=True)
link = models.CharField(max_length=256, blank=True)
educacion = models.CharField(max_length=256, blank=True)
experiencia = models.TextField(max_length=512, blank=True)
birthdate = models.DateField(blank=True, null=True)
#receiver(post_save, sender=User)
def update_profile_signal(sender, instance, created, **kwargs):
if created:
UserProfile.objects.create(user=instance)
So I achieved it by using commit=False in my form_valid method:
class UserDetail(FormMixin, DetailView):
template_name = "users/user_detail.html"
model = User
form_class = forms.UserPhotoForm
def get_success_url(self):
return reverse('users:user_detail', args=[str(self.get_object().pk)])
def get_context_data(self, **kwargs):
user = self.get_object()
form = forms.UserPhotoForm(instance=user)
context = super().get_context_data(**kwargs)
context['user_rating'] = CotizacionReview.objects.filter(cotizacion__user=self.get_object()).aggregate(Avg('nota'))
context['form'] = form
return context
def form_valid(self, form):
user_instance = form.save(commit=False)
user_instance.avatar = form.cleaned_data['avatar']
user_instance.id = self.get_object().userprofile.id
user_instance.save(update_fields=['avatar'])
return super(UserDetail, self).form_valid(form)
def post(self, request, *args, **kwargs):
form = self.get_form()
if form.is_valid():
return self.form_valid(form)
else:
return self.form_invalid(form)
But now I have another problem. Every time I update the photo, a new photo is saved to the database. Is there a way of doing this and deleting the old photo? or replacing it?
I have a ManytoMany field and the results of the CheckboxSelectMultiple don't get saved in the db and i don't understand why. It must be really simple but...
Here's the code:
models.py
class Person(models.Model):
last_name = models.CharField(max_length = 50)
first_name = models.CharField(max_length = 50)
def __str__(self):
return self.last_name +" "+self.first_name
class Event(models.Model):
owner = models.ForeignKey(User, on_delete=models.SET_NULL, null = True)
person = models.ManyToManyField(Person)
forms.py
class EventForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
self.user = kwargs.get('user',None)
#self.user = kwargs.pop('user',None)
super(EventForm, self).__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.form_method = 'POST'
#self.helper.form_action = reverse_lazy('simpleuser')
self.helper.add_input(Submit('submit', 'Submit', css_class='btn-success'))
class Meta:
model = Event
fields = ['person']
widgets ={
'person': forms.CheckboxSelectMultiple,
}
views.py
def uploadevent(request):
if request.method == "POST":
form =EventForm(request.POST)
if form.is_valid():
event = form.save(commit=False)
event.owner = request.user
event.save()
else:
form = EventForm()
return render...
See the response on this stackoverflow question: Saving Many To Many data via a modelform in Django
Quoting the OP:
When using commit=False, you have to call save_m2m()
m2m relationships require the parent object to be saved first, which
you are not doing by using commit=False
Just add this line below event.save()
if form.is_valid():
event = form.save(commit=False)
event.owner = request.user
event.save()
form.save_m2m()
Ref: https://docs.djangoproject.com/en/2.0/topics/forms/modelforms/#the-save-method
To save manytomany you should call save_m2m() when using commit=False (check details here):
if form.is_valid():
event = form.save(commit=False)
event.owner = request.user
event.save()
form.save_m2m()
I'm trying to upload an image. This is an avatar image for the profile of the user.
Currently, the form return no error, but I have nothing written on my database or in my folder media/avatar/.
What's wrong ?
My view :
def view_avatar(request):
if request.method == 'POST':
form = UploadFileForm(request.POST, request.FILES, instance=request.user.profile)
if form.is_valid():
form.save()
else:
form = UploadFileForm(instance=request.user.profile)
return render(request, 'avatar.html', locals())
My form :
class UploadFileForm(forms.ModelForm):
class Meta:
model = Profile
fields = ('avatar',)
My model :
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
birthdate = models.DateField(null=True, blank=True)
avatar = models.ImageField(upload_to='media/avatar/', blank=True, null=True)
It's because the form which you are using is inherited from forms.Form , you need to use forms.ModelForm for saving the instance directly.
Change this line,
class UploadFileForm(forms.ModelForm):
class Meta:
model = Profile
fields = ('avatar', )
def save(self, *args, **kwargs):
profile, created = Profile.objects.get_or_create(user=self.user)
profile.avatar = self.cleaned_data['avatar']
profile.save()
return profile
Also, edit in your views like this,
if form.is_valid():
file = form.save(commit=False)
file.user = request.user
file.save()
For making a profile you can use signals.
This way whenever a new user been added, a profile will be generated for that user automatically
Your models.py:
from django.conf import settings
from django.db.models.signals import post_save
class Profile(models.Model):
user = models.OneToOneField(settings.AUTH_USER_MODEL)
birthdate = models.DateField(null=True, blank=True)
avatar = models.ImageField(upload_to='media/avatar/%y/%m/%d', blank=True, null=True)
def post_save_profile(sender, instance, created, *args, **kwargs):
if created:
try:
Profile.objects.create(user=instance)
except:
pass
post_save.connect(post_save_profile, sender=settings.AUTH_USER_MODEL)
and for updating the information like birthday and avatar you can use ModelForm.
forms.py:
class UploadFileForm(forms.ModelForm):
class Meta:
model = Profile
fields = ('birthdate', 'avatar')
def __init__(self, *args, **kwargs):
super(UploadFileForm, self).__init__(*args, **kwargs)
self.fields['avatar'].required = False
your views.py:
def view_avatar(request):
user = request.user
if request.method == "POST":
form = UploadFileForm(request.POST, request.FILES, instance=user.profile)
if form.is_valid():
form.save()
for avatar in template you can use this:
<img src="{% if user.profile.avatar %}{{ user.profile.avatar.url }}{% else %}{% static 'no-avatar.jpg' %}{% endif %}"><i></i>
You can write custom Save method for this like this.
View:
def view_avatar(request):
if request.method == 'POST':
form = UploadFileForm(request.POST, request.FILES, user=request.user)
if form.is_valid():
form.save()
else:
form = UploadFileForm()
return render(request, 'avatar.html', locals())
Form:
class UploadFileForm(forms.Form):
class Meta:
model = Profile
fields = ('avatar', )
def __init__(self, *args, **kwargs):
self.user = kwargs.pop('user', None)
super(UploadFileForm, self).__init__(*args, **kwargs)
self.fields['avatar'].required = False
avatar = forms.FileField()
def save(self, *args, **kwargs):
user_profile, created = Profile.objects.get_or_create(user=self.user)
user_profile.avatar = self.cleaned_data.get('avatar')
user_profile.save()
return user_profile
I forgot the enctype !
The solution was :
<form method="POST" action="" enctype="multipart/form-data">
I have a model Course that has the following attr:
class Course(models.Model):
user= models.ForeignKey(
User,
on_delete=models.CASCADE,
)
# email= models.EmailField(default=user.email)
courseName= models.CharField(max_length=20, blank=False)
class Meta:
unique_together= ('user','courseName',)
def __str__(self):
return self.courseName
I have created a form where I want the user to enter just the courseName and after they POST it, I will add the requested user in the model as well.
This is my form which is getting passed on to the template via my ListView
forms.py
class CourseForm(forms.ModelForm):
class Meta:
model= Course
fields = ['courseName']
**Here is my views.py where I am struggling with **
class CoursesListView(ListView, FormMixin):
model = Course
form_class = CourseForm
template_name = "userApp/courseList.html"
def get_queryset(self):
return Course.objects.filter(user__exact=self.request.user)
def get_context_data(self,*args,**kwargs):
context= super(CoursesListView,self).get_context_data(*args, **kwargs)
context['courseForm'] = self.form_class
return context
def post(self,request, *args, **kwargs):
form = self.form_class(request.POST)
user = User.objects.get(username__exact=self.request.user)
**I want to add the user to my model.user field here**
return self.get(redirect, *args, **kwargs)
def get(self,request, *args, **kwargs):
self.object=None
self.form = self.get_form(self.form_class)
return ListView.get(self, request, *args, **kwargs)
So basically my question is how can I add the user in my model before calling form.is_valid().
something like this?
def post(self,request, *args, **kwargs):
form_data = copy.copy(request.POST)
form_data['user'] = User.objects.get(username__exact=self.request.user).pk
form = self.form_class(form_data)
# form handling follows
return self.get(redirect, *args, **kwargs)
This Answer was suggested by a user who deleted this answer. Didnt get his user id but whoever you were thanks a lot for the help!!!
Just use form.save(commit=False) and then make the necessary changes.
def post(self,request, *args, **kwargs):
form = self.form_class(request.POST)
if form.is_valid():
user = User.objects.get(username__exact=self.request.user)
instance = form.save(commit=False)
instance.user = user
instance.save()