MY CreateView worked fine but when I try to update this form data, it gives blank form. No data was shown. Here is my views.py:
#require_authenticated_permission(
'member.add_person')
class PersonCreate( FormsetMixin, CreateView):
template_name = 'member/person_form.html'
model = Person
form_class = MemberForm
formset_class = PersonFormSet
#require_authenticated_permission(
'member.change_person')
class PersonUpdate( FormsetMixin, UpdateView):
template_name = 'member/person_form.html'
model = Person
form_class = MemberForm
formset_class = PersonFormSet
Here is my formset:
class MemberForm(ModelForm):
class Meta:
model = Person
exclude = ('user',)
def __init__(self, *args, **kwargs):
super(MemberForm, self).__init__(*args, **kwargs)
self.fields['name'].widget.attrs['placeholder'] = 'Your full name'
self.fields['tele_land'].label = 'Land phone'
self.fields['tele_cell'].label = 'Cell phone'
self.fields['passing_year'].label = 'Passing year'
self.fields['passing_year'].help_text = 'According to your session year'
def save(self, request, commit=True):
person = super().save(commit=False)
if not person.pk:
person.user = get_user(request)
if commit:
person.save()
self.save_m2m()
return person
class ChildrenForm(ModelForm):
class Meta:
model = Children
fields = '__all__'
PersonFormSet = inlineformset_factory(Person, Children, extra=0, min_num=1, fields=('child_name', 'child_birth_date','blood_group' ))
Url:
url(r'^person/create/$', views.PersonCreate.as_view(), name='person-create'),
url(r'^person/(?P<slug>[\w\-]+)/update/$', views.PersonUpdate.as_view(), name='person-update'),
My formset mixin:
class FormsetMixin(object):
object = None
def get(self, request, *args, **kwargs):
if getattr(self, 'is_update_view', False):
self.object = self.get_object()
form_class = self.get_form_class()
form = self.get_form(form_class)
formset_class = self.get_formset_class()
formset = self.get_formset(formset_class)
return self.render_to_response(self.get_context_data(form=form, formset=formset))
def post(self, request, *args, **kwargs):
if getattr(self, 'is_update_view', False):
self.object = self.get_object()
form_class = self.get_form_class()
form = self.get_form(form_class)
formset_class = self.get_formset_class()
formset = self.get_formset(formset_class)
if form.is_valid() and formset.is_valid():
return self.form_valid(form, formset)
else:
return self.form_invalid(form, formset)
def get_formset_class(self):
return self.formset_class
def get_formset(self, formset_class):
return formset_class(**self.get_formset_kwargs())
def get_formset_kwargs(self):
kwargs = {
'instance': self.object
}
if self.request.method in ('POST', 'PUT'):
kwargs.update({
'data': self.request.POST,
'files': self.request.FILES,
})
return kwargs
def form_valid(self, form, formset):
self.object = form.save(self.request)
formset.instance = self.object
formset.save()
return redirect(self.object.get_absolute_url())
def form_invalid(self, form, formset):
return self.render_to_response(self.get_context_data(form=form, formset=formset))
Why my UpdateView gives blank form?
Proably because getattr(self, 'is_update_view', False) always is false so you're not loading any self.object.
I have for similar situations used ModelFormSetView from django-extra-views. It's easy to use if you already understand Django class based views and will save you a lot of code to write and maintain yourself.
BTW you should implement get_queryset and prefetch the Children.
Use in UpdateView: is_update_view = True. It will be like
class PersonUpdate( FormsetMixin, UpdateView):
template_name = 'member/person_form.html'
model = Person
is_update_view = True
form_class = MemberForm
formset_class = PersonFormSet
Related
I would like to get initial value on the following form but i get error : name 'user' is not defined. I don't know how to get user.username.
class InvoiceForm(ModelForm):
date = forms.DateField(widget=DateInput)
company = forms.CharField(initial={"company": user.username})
class Meta:
model = Invoice
fields = "__all__"
and the view to create the invoice is :
class InvoiceCreate(CreateView):
form_class = InvoiceForm
model = Invoice
template_name = "sales/invoice_form.html"
def get_success_url(self, request):
return reverse_lazy('invoice_details', kwargs={'pk' : self.object.pk})
def get(self, request, *args, **kwargs):
self.object = None
form_class = self.get_form_class()
form = self.get_form(form_class)
formset = InvoiceItemFormSet()
products = list(Product.objects.values())
return self.render_to_response(
self.get_context_data(form=form,formset=formset, products=products))
def post(self, request, *args, **kwargs):
self.object = None
form_class = self.get_form_class()
form = self.get_form(form_class)
formset = InvoiceItemFormSet(self.request.POST)
if (form.is_valid() and formset.is_valid()):
return self.form_valid(form, formset)
else:
return self.form_invalid(form, formset)
def form_valid(self, form, formset):
self.object = form.save()
formset.instance = self.object
formset.save()
try:
addmore = self.request.GET["addmore"]
if addmore == "True":
return redirect("update_invoice", pk=self.object.id)
except Exception as e:
pass
return HttpResponseRedirect(self.get_success_url())
def form_invalid(self, form, formset):
return self.render_to_response(self.get_context_data(form=form, formset=formset))
I would like to get initial value on the following form but i get error : name 'user' is not defined. I don't know how to get user.username.
You had that error because there is no user at that point in the class level definition:
company = forms.CharField(initial={"company": user.username})
change to:
company = forms.CharField()
then is views.py add this method to InvoiceCreate class:
def get_initial(self):
return {"company": self.request.user}
i want to filter the comments by user and by post. would you like to tell me how can i filter the comment using get_context_data. i am getting this error with that code 'NewsDetailView' object has no attribute 'get_object_or_404' how can i solve this issue?
models.py
class Comment(models.Model):
article = models.ForeignKey(Article, on_delete=models.CASCADE)
commentator = models.ForeignKey(User, on_delete=models.CASCADE)
content = models.TextField(max_length=200)
created_on = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.article.title
views.py
class NewsDetailView(LoginRequiredMixin, DetailView):
model = Article
form_class = CommentForm
template_name = 'news/news_detail.html'
def get(self, request, *args, **kwargs):
self.object = self.get_object_or_404(User, username=self.kwargs.get('username'))
self.object = self.get_object_or_404(Article, pk=self.kwargs.get('pk'))
return super().get(request, *args, **kwargs)
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['comments'] = self.object
context['form'] = CommentForm()
return context
def post(self, request, *args, **kwargs):
if request.method == 'POST':
form = CommentForm(request.POST)
form.instance.article = Article.objects.get(
pk=self.kwargs.get('pk'))
form.instance.commentator = self.request.user
form.save()
return redirect('news:news-detail', pk=self.kwargs.get('pk'))
else:
return redirect('news:news-detail', pk=self.kwargs.get('pk'))
The NewsDetailView indeed has no get_object_or_404, so using self.get_object_or_404 does not make any sense. Furthermore it is not necessary at all.
You can obtain the Comments that belong to a user with the username with self.object.comment_set.filter(commentator__username=username)
You can further use the FormMixin [Django-doc] to avoid some boilerplate code to construct the form and redirect to the success url:
from django.urls import reverse
from django.views.generic.edit import FormMixin
class NewsDetailView(LoginRequiredMixin, FormMixin, DetailView):
model = Article
form_class = CommentForm
template_name = 'news/news_detail.html'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['comments'] = self.object.comment_set.filter(
commentator__username=self.kwargs['username']
)
return context
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)
def form_valid(self, form):
form.instance.article_id = self.kwargs['pk']
form.instance.commentator = self.request.user
form.save()
return super().form_valid(form)
def form_invalid(self, form):
self.object = self.get_object()
return super().form_invalid(form)
def get_success_url(self):
return reverse('news:news-detail', kwargs={'pk': self.kwargs['pk']})
I am new in Django (1.9)
I have a NOT NULL constraint failed when I save my ModelForm and i don't understand why ...
I propose to the user a form to post a comment and the only field include in the form is "text", i want to set the excluded fields in my view after the validation and before save in database
Models.py :
class Commentaire(models.Model):
text = RichTextField()
pub_date = models.DateTimeField()
author = models.ForeignKey(User)
post = models.ForeignKey(Post)
publish = models.BooleanField()
def __str__(self):
return "/%s/%s" % (self.pub_date,self.author.username)
class Meta:
ordering = ["-pub_date"]
Forms.py :
class CommentaireForm(forms.ModelForm):
class Meta:
model = Commentaire
fields = ['text']
Views.py :
class PostDetail(View):
def get(self, request, *args, **kwargs):
view = PostDisplay.as_view()
return view(request, *args, **kwargs)
def post(self, request, *args, **kwargs):
view = PostComment.as_view()
return view(request, *args, **kwargs)
class PostDisplay(DetailView):
model = Post
def get_context_data(self, **kwargs):
context = super(PostDisplay, self).get_context_data(**kwargs)
context['form'] = CommentaireForm()
return context
class PostComment(SingleObjectMixin, FormView):
template_name = 'blogengine/post_detail.html'
form_class = CommentaireForm
model = Post
def post(self, request, *args, **kwargs):
#if not request.user.is_authenticated():
# return HttpResponseForbidden()
self.object = self.get_object()
form = self.get_form()
if form.is_valid():
return self.form_valid(form)
else:
return self.form_invalid(form)
def form_valid(self, form):
"""
If the form is valid, redirect to the supplied URL
"""
form.save(commit=False)
form.post = self.object
form.author = self.request.user
form.pub_date = datetime.now()
form.publish = True
form.save()
return HttpResponseRedirect(self.get_success_url())
When Django save the form, i have this exception :
NOT NULL constraint failed: blogengine_commentaire.pub_date
The values i set for the excluded fields (author, post, pub_date) in "form_valid" are not taken into account, they seem to stay NULL
Could you explain me why because i am lost ?
Thanks
You need to rewrite form_valid method like that
def form_valid(self, form):
"""
If the form is valid, redirect to the supplied URL
"""
model_instance = form.save(commit=False)
model_instance.post = self.object
model_instance.author = self.request.user
model_instance.pub_date = datetime.now()
model_instance.publish = True
model_instance.save()
return HttpResponseRedirect(self.get_success_url())
Because save(commit=False) will return you an Post instance that you then need to populate and save.
I have a model PSG which has a relation to another model SubPSG. PSGs can have several SubPSGs. I'm using inline formsets to create SubPSGs as I create the PSGs. The CreateView works okay but the UpdateView is not working as expected. The UpdateView is as follows:
class PSGUpdate(GroupRequiredMixin, UpdateView):
model = PSG
form_class = PSGForm
template_name = "management/psg_update_form.html"
success_url = reverse_lazy('dashboard')
group_required = [u"admin", "manager"]
def get_context_data(self, **kwargs):
context = super(PSGUpdate, self).get_context_data(**kwargs)
subpsgs = SubPSG.objects.filter(psg=self.object).values()
sub_formset = inlineformset_factory(PSG, SubPSG, fields=('name',), can_delete=True,
extra=len(subpsgs), formset=BaseSubPSGFormset,)
context['formset'] = sub_formset(instance=self.object, initial=subpsgs, prefix='psg')
return context
def post(self, request, *args, **kwargs):
self.object = self.get_object()
subpsgs = SubPSG.objects.filter(psg=self.object).values()
psg_form = PSGForm(request.POST, instance=self.object)
sub_formset = inlineformset_factory(PSG, SubPSG, fields=('name',), can_delete=True, extra=len(subpsgs), formset=BaseSubPSGFormset)
formset = sub_formset(self.request.POST, instance=self.object, prefix='psg')
if all([psg_form.is_valid(), formset.is_valid()]):
return self.form_valid(psg_form, formset)
else:
return self.form_invalid(psg_form, formset)
def form_valid(self, form, formset):
if all([formset.is_valid() and form.is_valid()]):
self.object = form.save()
instances = formset.save(commit=False)
for form in instances:
form.psg = self.object
form.save()
return HttpResponseRedirect(self.get_success_url())
else:
return super(PSGUpdate, self).form_valid(form)
When I try updating a SubPSG formset, a new object is created instead of the edit taking place. Moreover, duplicates of the other formsets are also created.
What I'm I doing wrong??
I have been trying to do Django class-based CreateView and UpdateView with multiple inline formsets
CreateView works fine but UpdateView is not working properly, If anyone tried UpdateView with multiple inline formsets, anyone tried pls share updateview code snippet.
# models.py
from django.db import models
class Recipe(models.Model):
title = models.CharField(max_length=255)
description = models.TextField()
class Ingredient(models.Model):
recipe = models.ForeignKey(Recipe)
description = models.CharField(max_length=255)
class Instruction(models.Model):
recipe = models.ForeignKey(Recipe)
number = models.PositiveSmallIntegerField()
description = models.TextField()
# forms.py
from django.forms import ModelForm
from django.forms.models import inlineformset_factory
from .models import Recipe, Ingredient, Instruction
class RecipeForm(ModelForm):
class Meta:
model = Recipe
IngredientFormSet = inlineformset_factory(Recipe, Ingredient, extra=0)
InstructionFormSet = inlineformset_factory(Recipe, Instruction, extra=0)
# views.py
from django.http import HttpResponseRedirect
from django.views.generic.edit import CreateView, UpdateView
from django.shortcuts import get_object_or_404
from .forms import IngredientFormSet, InstructionFormSet, RecipeForm
from .models import Recipe
class RecipeCreateView(CreateView):
template_name = 'recipe_add.html'
model = Recipe
form_class = RecipeForm
success_url = '/account/dashboard/'
def get(self, request, *args, **kwargs):
self.object = None
form_class = self.get_form_class()
form = self.get_form(form_class)
ingredient_form = IngredientFormSet()
instruction_form = InstructionFormSet()
return self.render_to_response(
self.get_context_data(form=form,
ingredient_form=ingredient_form,
instruction_form=instruction_form))
def post(self, request, *args, **kwargs):
self.object = None
form_class = self.get_form_class()
form = self.get_form(form_class)
ingredient_form = IngredientFormSet(self.request.POST)
instruction_form = InstructionFormSet(self.request.POST)
if (form.is_valid() and ingredient_form.is_valid() and
instruction_form.is_valid()):
return self.form_valid(form, ingredient_form, instruction_form)
else:
return self.form_invalid(form, ingredient_form, instruction_form)
def form_valid(self, form, ingredient_form, instruction_form):
self.object = form.save()
ingredient_form.instance = self.object
ingredient_form.save()
instruction_form.instance = self.object
instruction_form.save()
return HttpResponseRedirect(self.get_success_url())
def form_invalid(self, form, ingredient_form, instruction_form):
return self.render_to_response(
self.get_context_data(form=form,
ingredient_form=ingredient_form,
instruction_form=instruction_form))
class RecipeUpdateView(UpdateView):
template_name = 'recipe_add.html'
model = Recipe
form_class = RecipeForm
def get_success_url(self):
self.success_url = '/account/dashboard/'
return self.success_url
def get_context_data(self, **kwargs):
context = super(RecipeUpdateView, self).get_context_data(**kwargs)
if self.request.POST:
context['form'] = RecipeForm(self.request.POST, instance=self.object)
context['ingredient_form'] = IngredientFormSet(self.request.POST, instance=self.object)
context['instruction_form'] = InstructionFormSet(self.request.POST, instance=self.object)
else:
context['form'] = RecipeForm(instance=self.object)
context['ingredient_form'] = IngredientFormSet(instance=self.object)
context['instruction_form'] = InstructionFormSet(instance=self.object)
return context
def post(self, request, *args, **kwargs):
self.object = None
form_class = self.get_form_class()
form = self.get_form(form_class)
ingredient_form = IngredientFormSet(self.request.POST)
instruction_form = InstructionFormSet(self.request.POST)
if (form.is_valid() and ingredient_form.is_valid() and
instruction_form.is_valid()):
return self.form_valid(form, ingredient_form, instruction_form)
else:
return self.form_invalid(form, ingredient_form, instruction_form)
def form_valid(self, form, ingredient_form, instruction_form):
self.object = form.save()
ingredient_form.instance = self.object
ingredient_form.save()
instruction_form.instance = self.object
instruction_form.save()
return HttpResponseRedirect(self.get_success_url())
def form_invalid(self, form, ingredient_form, instruction_form):
return self.render_to_response(
self.get_context_data(form=form,
ingredient_form=ingredient_form,
instruction_form=instruction_form))
Thanks in advance.
I don't think that the regular form of the updateview has to be added to the context because it is there anyways. A working Updateview with inlineformsets could be achieved less complicated. I based this on this Question
class RecipeUpdateView(UpdateView):
model = Recipe
form_class = RecipeUpdateForm
success_url = "/foo/"
def get_success_url(self):
self.success_url = '/account/dashboard/'
return self.success_url
def get_object(self):
return #your object
def get_context_data(self, **kwargs):
context = super(RecipeUpdateView, self).get_context_data(**kwargs)
if self.request.POST:
context['ingredient_form'] = IngredientFormSet(self.request.POST, instance=self.object)
context['instruction_form'] = InstructionFormSet(self.request.POST, instance=self.object)
else:
context['ingredient_form'] = IngredientFormSet(instance=self.object)
context['instruction_form'] = InstructionFormSet(instance=self.object)
return context
def form_valid(self, form):
context = self.get_context_data()
ingredient_form = context['ingredient_form']
instruction_form = context['instruction_form']
if ingredient_form.is_valid() and instruction_form.is_valid():
self.object = form.save()
ingredient_form.instance = self.object
ingredient_form.save()
instruction_form.instance = self.object
instruction_form.save()
return self.render_to_response(self.get_context_data(form=form))
My guess is that you can't do
self.object = None
on overwritten post method in a UpdateView. So, try
self.object = self.get_object()
instead, once you already have an object instance in this case.
I'm not sure if you've found an answer, but I have a working version of UpdateView documented in my answer found here:
UpdateView with inline formsets trying to save duplicate records?
So I recognize the models form this post. To get UpdateView working properly you are going to have to do at least two, maybe three things:
Update the self.object = self.get_object() -- after that, your ability to dynamically add should work.
To get the dynamic deletes updating properly, you will need to alter the template with form.DELETE (in two places, the ingredients and the instructions).
{{ form.description }}
{% if form.instance.pk %}{{ form.DELETE }}{% endif %}
Not sure it was necessary but I added can_delete to the factory too.
IngredientFormSet = inlineformset_factory(Recipe, Ingredient, fields=('description',), extra=3, can_delete=True)
InstructionFormSet = inlineformset_factory(Recipe, Instruction, fields=('number', 'description',), extra=1, can_delete=True)