Django - UpdateView with multi model forms - one submit - django

I'm developing the web site where users can register and edit their information. I would like to use multi form in the single page. I can develop register form using CreateView. But when I use UpdateVIew, I don't know how to add initial value in multi form.
My code(view.py) is below. I can add form data as initial value, but not form2. Please tell me how to add form2 data as initial value.
▪️view.py
class UserUpdate(OnlyYouMixin, generic.UpdateView):
model = Profile
form_class = ProfileForm
second_form_class = ProfileNearStationForm
template_name = 'accounts/user_form.html'
def get_context_data(self, **kwargs):
context = super(UserUpdate, self).get_context_data(**kwargs)
if 'form' not in context:
context['form'] = self.form_class(self.request.GET, instance=self.request.user)
if 'form2' not in context:
context['form2'] = self.second_form_class(self.request.GET, instance=self.request.user)
return context
def get(self, request, *args, **kwargs):
super(UserUpdate, self).get(request, *args, **kwargs)
form = self.form_class
form2 = self.second_form_class
return self.render_to_response(self.get_context_data())
def post(self, request, *args, **kwargs):
self.object = self.get_object()
form = self.form_class(request.POST, instance=self.request.user)
form2 = self.second_form_class(request.POST, instance=self.request.user)
if form.is_valid() and form2.is_valid():
profile = form.save(commit=False)
profile.save()
station = form2.save(commit=False)
station.user = user
station.save()
messages.success(self.request, 'Settings saved successfully')
return HttpResponseRedirect(self.get_success_url())
else:
return self.render_to_response(
self.get_context_data(form=form, form2=form2))
def get_success_url(self):
return resolve_url('accounts:user_form', pk=self.kwargs['pk'])
▪️form.py
class ProfileForm(forms.ModelForm):
name = forms.CharField(required=True)
tel_number = forms.CharField(required=True)
class Meta:
model = Profile
fields = ('name', 'tel_number')
class ProfileNearStationForm(forms.ModelForm):
prefecture = forms.ModelChoiceField(queryset=areas_model.Prefecture.objects.all(), required=True)
railway = forms.ModelChoiceField(queryset=areas_model.Railway.objects.none(), required=True)
station = forms.ModelChoiceField(queryset=areas_model.Station.objects.none(), required=True)
memo = forms.CharField(required=False)
class Meta:
model = ProfileNearStation
fields = ('prefecture', 'railway', 'station', 'memo')
▪️model.py
class Profile(models.Model):
user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
name = models.CharField(max_length=255)
tel_number = models.CharField(max_length=13)
class ProfileNearStation(models.Model):
prefecture = models.ForeignKey(areas_model.Prefecture, on_delete=models.CASCADE)
railway = models.ForeignKey(areas_model.Railway, on_delete=models.CASCADE)
station = models.ForeignKey(areas_model.Station, on_delete=models.CASCADE)
memo = models.CharField(max_length=255, null=True)
user = models.ForeignKey(User, on_delete=models.CASCADE)

Related

Formset not submitting uploaded picture?

I have a view to allow courses to created by users on my app, which am currently having a challenge uploading a course cover(pic) when users create a new course, when course is created all the fields of the course detail get submitted to the data base except the picture data, why is the picture not being submitted, can some one please help me. as this has been a challenge for me for the past week!
This is my view to create a new course!.
class OwnerMixin(object):
def get_queryset(self):
qs = super(OwnerMixin, self).get_queryset()
return qs.filter(owner=self.request.user)
class OwnerEditMixin(object):
def form_valid(self, form):
form.instance.owner = self.request.user
return super(OwnerEditMixin, self).form_valid(form)
class OwnerCourseMixin(OwnerMixin):
model = Course
fields = ['subject', 'title', 'slug','overview','pic']
success_url = reverse_lazy('courses:manage_course_list')
class OwnerCourseEditMixin(OwnerCourseMixin):
fields = ['subject', 'title','slug', 'overview','pic']
success_url = reverse_lazy('courses:manage_course_list')
template_name = 'manage/module/formset.html'
class ManageCourseListView(OwnerCourseMixin,ListView):
template_name ='courses/course_list.html'
class CourseCreateView(OwnerCourseEditMixin,OwnerEditMixin,CreateView,):
pass
permission_required = 'courses.add_course'
class CourseModuleUpdateView(TemplateResponseMixin, View):
template_name = 'manage/module/formset.html'
course = None
def get_formset(self, data=None,):
return ModuleFormSet(instance=self.course,data=data,)
def get_form(self, data=None):
return RequirementFormset(instance=self.course,data=data)
def get_forms(self, data=None):
return WhatYouWillLearnFormset(instance=self.course,data=data)
def dispatch(self, request, pk):
self.course = get_object_or_404(Course,id=pk,owner=request.user)
return super(CourseModuleUpdateView, self).dispatch(request, pk)
def get(self, request, *args, **kwargs):
formset = self.get_formset()
form = self.get_form()
forms = self.get_forms()
return self.render_to_response({'course':self.course,
'formset':formset,'form':form,'forms':forms,})
def post(self, request, *args, **kwargs):
formset = self.get_formset(data=request.POST,)
form = self.get_form(data=request.POST,)
forms = self.get_forms(data=request.POST,)
if formset.is_valid():
formset.save()
if form.is_valid():
form.save()
if forms.is_valid():
forms.save()
return redirect('courses:manage_course_list')
return self.render_to_response({'course': self.course,
'formset':formset,})
Here is my model, am using formset to create course and also
class Course(models.Model):
owner = models.ForeignKey(settings.AUTH_USER_MODEL,\
related_name='courses_created', on_delete=models.CASCADE)
subject = models.ForeignKey(Subject,related_name='courses', on_delete=models.CASCADE)
title = models.CharField(max_length=200)
pic = models.ImageField(upload_to="course_pictures", blank=True,null=True)
slug = models.SlugField(max_length=200, unique=True,blank=True)
overview = models.TextField()
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
students = models.ManyToManyField(settings.AUTH_USER_MODEL,related_name='courses_joined',blank=True)
class Meta:
ordering = ['-created']
def __str__(self):
return self.title
Here is my formset
class CourseForm(forms.ModelForm):
class Meta:
model = Course
fields = ['pic']
ModuleFormSet = inlineformset_factory(Course,
Module,
fields=['title','description',],
extra=2,
can_delete=True)
RequirementFormset = inlineformset_factory(Course,
Requirement,
fields=['requirements'],
extra=4,can_delete=True)
WhatYouWillLearnFormset = inlineformset_factory(Course,
WhatYouWillLearn,
fields=['hightlights'],
extra=4,can_delete=True)
I think the attribute enctype="multipart/form-data" is missing in the form tag in the template file.
<form method="post" enctype="multipart/form-data">
...
...
</form>

Django form : how I can get a second user with instance?

I try to understand how I can fill automatically the seller user field:
Assume I got private group with two users (one is consumer the other is seller)
Only consumer can have access to this form.
So form.instance.consumer = self.request.user works well.
form.instance.channel = self.object works well also.
In my forms I'm using crispy forms and it's only display two fields (rate and comment).
But my question is how I can get seller user? I'm beginning with Django :)
#Channel REVIEW
#method_decorator(login_required(login_url='/cooker/login'),name="dispatch")
class ChannelReview(generic.DetailView, FormMixin):
model = Channel
context_object_name = 'channel'
template_name = 'channel_review.html'
form_class = ChannelRatingForm
def get_context_data(self, **kwargs):
context = super(ChannelReview, self).get_context_data(**kwargs)
context['form'] = self.get_form()
return context
def form_valid(self, form):
if form.is_valid():
form.instance.channel = self.object
form.instance.seller = seller #### Here is my question
form.instance.consumer = self.request.user
form.save()
return super(ChannelReview, self).form_valid(form)
else:
return super(ChannelReview, self).form_invalid(form)
def post(self,request,*args,**kwargs):
self.object = self.get_object()
form = self.get_form()
if form.is_valid():
return self.form_valid(form)
else:
return self.form_valid(form)
def get_success_url(self):
return reverse('channel:channel_detail',kwargs={"slug":self.object.slug})
def get(self,request,*args,**kwargs):
self.object = self.get_object()
if not (request.user == self.object.consumer or request.user == self.object.seller):
return HttpResponseRedirect('/')
return super(ChannelReview, self).get(request,*args,**kwargs)
class Channel(models.Model):
consumer = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name="channel_consumer", blank=True, null=True)
name = models.CharField(max_length=10)
seller = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name="channel_seller")
class Rating(models.Model):
channel = models.OneToOneField(Channel,on_delete=models.CASCADE,related_name="rating_channel")
consumer = models.ForeignKey(settings.AUTH_USER_MODEL, blank=True, null=True, on_delete=models.CASCADE,related_name='rating_consumer')
seller = models.ForeignKey(settings.AUTH_USER_MODEL, blank=True, null=True, on_delete=models.CASCADE,related_name='rating_seller')
is_rated = models.BooleanField('Already rated', default=True)
comment = models.TextField(max_length=200)
publishing_date = models.DateTimeField(auto_now_add=True)
ratesugar = models.IntegerField(validators=[MinValueValidator(1), MaxValueValidator(5)])
You can use the .update to update a model object, like this in case.
https://www.kite.com/python/examples/5166/django-models-update-a-model-object-without-loading-the-object-from-the-database

Django: When form is submitted, auto-fill a field

I'm looking to auto-fill a field when I submit a form so it is identical to another field.
I have a createView with a modelForm. The idea is the user writes his/hers name, which then when submitted fills out the display_name field as well. I know it's weird "why have two of the same", but it has a purpose.
Here's my code - I'm guessing I need to create a context that gets the display_name and sets it to be equal to the name?
views.py
class CreateIdentity(CreateView):
template_name = 'admin/CreateIdentity.html'
model = Identity
form_class = CreateIdentityForm
queryset = Identity.objects.all()
def get_context_data(self, **kwargs):
context = super(CreateIdentity, self).get_context_data(**kwargs)
context["user_id"] = User.objects.get(username=self.request.user)
return context
def form_valid(self, form):
form.instance.user_id = self.request.user
return super().form_valid(form)
def get_success_url(self):
return reverse_lazy("view_identity_created_with_slug", kwargs={'slug': self.object.slug})
models.py
class Identity(models.Model):
name = models.CharField(max_length=200, null=False)
display_name = models.CharField(max_length=200)
slug = models.SlugField(max_length=100)
user_id = models.ForeignKey(User, db_column="username", on_delete=models.CASCADE, null=False)
forms.py
class CreateIdentityForm(forms.ModelForm):
name = forms.CharField(required=True, widget=forms.TextInput(attrs={
'spellcheck': "false",
"class": "identity_name_input",
'placeholder': "Write your identity"}),
)
class Meta:
model = Identity
fields = ["name"]
def __str__(self):
return self.name
In the .form_valid(…) [Django-doc], you can specify the display_name based on the cleaned data of the form:
class CreateIdentity(CreateView):
template_name = 'admin/CreateIdentity.html'
model = Identity
form_class = CreateIdentityForm
queryset = Identity.objects.all()
def get_context_data(self, **kwargs):
context = super(CreateIdentity, self).get_context_data(**kwargs)
context["user_id"] = User.objects.get(username=self.request.user)
return context
def form_valid(self, form):
form.instance.user_id = self.request.user
form.instance.display_name = form.cleaned_data['name']
return super().form_valid(form)
def get_success_url(self):
return reverse_lazy("view_identity_created_with_slug", kwargs={'slug': self.object.slug})

UNIQUE constraint failed: auth_user.username while updating user info

I am trying to give a user the option to change his/her first/last name through a ModelForm. When I press submit, I get hit with the UNIQUE constraint failed: auth_user.username error. Here are my codes:
students/forms.py:
class EditProfileForm(UserChangeForm):
def clean_password(self):
# Overriding the default method because I dont want user to change
# password
pass
class Meta:
model = User
fields = (
'first_name',
'last_name',
)
students/views.py:
User = get_user_model()
def student_profile_view(request, slug):
if request.method == 'GET':
# forms
edit_name_form = EditProfileForm(instance=request.user)
context = {
'edit_name_form': edit_name_form,
}
return render(request, "students/profile.html", context)
class ChangeNameView(SuccessMessageMixin, UpdateView):
template_name = 'students/edit_profile.html'
model = User
form_class = EditProfileForm
success_message = "Your name has been updated"
def post(self, request, *args, **kwargs):
form = self.get_form()
if form.is_valid():
form.instance.student_profile = StudentProfile.objects.get(slug=request.user.student_profile.slug)
return self.form_valid(form)
else:
return self.form_invalid(form)
def form_valid(self, form):
"""If the form is valid, save the associated model."""
form.instance.username = self.request.user
self.object = form.save(commit=False)
return super().form_valid(form)
def get_success_url(self):
return reverse('students:student_profile_view', kwargs={'slug': self.object.student_profile.slug})
also fyi, User model is foreign key with StudentProfile.
students/models.py:
class StudentProfile(models.Model):
user = models.OneToOneField(User, related_name='student_profile', on_delete=models.CASCADE)
slug = models.SlugField(blank=True, unique=True)
avatar = models.ImageField(upload_to='student_profile/', null=True, blank=True)
description = models.CharField(max_length=120, null=True, blank=True)
objects = models.Manager()
def __str__(self):
return self.user.username
def get_absolute_url(self):
return reverse("students:student_profile_view", kwargs={"slug": self.slug})
I am pretty new to class based view so maybe I'm doing something wrong there?
I assume you do not have the user within the form so you need the form
def get_context_data (self, *args, **kwargs)
ctx = super().get_context_data(*args, **kwargs)
if self.request.method == 'POST':
ctx['form'] = EditProfileForm(instance=self.request.user)
and remove def form_valid()

Django model : save object with custom field

I would like to know how I can do that : save an object in my database through Django model with custom field.
This is my modelclass :
class Document(EdqmTable):
language = models.CharField(max_length=2, verbose_name=_('language'), choices=LANGUAGE_CHOICES, null=False)
format = models.CharField(max_length=10, verbose_name=_('format'), choices=FORMAT_CHOICES, null=False)
title = models.CharField(max_length=512, verbose_name=_('document title'), null=False)
publication = models.ForeignKey(Publication, verbose_name=_('publication title'), null=False,
related_name='documents')
class Meta:
verbose_name = _('document')
verbose_name_plural = _('documents')
def save(self, *args, **kwargs):
self.title = f"{self.publication.pub_id}-{self.format.upper()}"
super(Document, self).save(*args, **kwargs)
I have a save method, which let to define my field title with combination between two fields.
title field is hidden in my django form and is set automatically by Django.
But, up to now, it doesn't work because my object is not saved into my database. Something is wrong in my save function ?
EDIT :
I edited my post with forms.py file and my view :
The field document.title is used in my django forms with formset like this :
class PublicationForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['category'].empty_label = _('Select a category') # Modify initial empty_label
class Meta:
model = Publication
fields = ['title', 'pub_id', 'category']
class DocumentForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(DocumentForm, self).__init__(*args, **kwargs)
self.fields['title'].widget = forms.HiddenInput()
for key in self.fields:
self.fields[key].required = True
class Meta:
model = Document
fields = ['publication', 'language', 'format', 'title', 'upload']
DocumentFormSet = inlineformset_factory(Publication, Document, form=DocumentForm, extra=1)
And my view according to this part :
class PublicationCreateView(EdqmCreateView):
""" Create publication with document form through formset """
model = Publication
template_name = 'freepub/publication_form.html'
def get_context_data(self, **kwargs):
context = super(PublicationCreateView, self).get_context_data(**kwargs)
context['document_form'] = DocumentFormSet(self.request.POST or None, self.request.FILES or None)
return context
def form_valid(self, form):
context = self.get_context_data()
document = context['document_form']
if document.is_valid():
self.object = form.save()
document.instance = self.object
document.save()
return super(PublicationCreateView, self).form_valid(form)
def get_success_url(self):
return reverse('publication-list-crud')