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.
Related
So I have this view:
class ProfileView(generic.UpdateView):
model = User
fields = [....]
template_name_suffix = '_update_form'
success_url = reverse_lazy('home')
def post(self, request, *args, **kwargs):
self.object = self.get_object()
self.object.is_active = False
return super().post(request, *args, **kwargs)
when the user saves his data on update, I want some fields to be completed automatically, such as is_active = False.
I used the approach above but my inserted fields aren't changed.
Why and how can I get the desired result?
Thanks.
There will be two objects here: the one wrapped in the form, and the one you use in the .post method, and you save the one in the form.
You can override the .form_valid(…) method [Django-doc]:
class ProfileView(generic.UpdateView):
model = User
fields = # …
template_name_suffix = '_update_form'
success_url = reverse_lazy('home')
def form_valid(self, form):
form.instance.is_active = False
return super().form_valid(form)
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']})
There is a form that is rendered by url
url(r'kredit/(?P<credit_slug>[-\.\w\d]+)/$', CreditDetail.as_view(), name='credit_detail'),
urls
url(r'kredit/(?P<credit_slug>[-\.\w\d]+)/$', CreditDetail.as_view(), name='credit_detail'),
url(r'kredit_request/$', CreditOnlineRequestView.as_view(), name='credit_request'),
The form is processed in the CreditOnlineRequestView(CreateView) view.
It is necessary to pull out the credit_slug from CreditDetail view in it (here the form was drawn)
views
class CreditDetail(FormView):
form_class = CreditPaymentForm
template_name = 'credits/credit_detail.html'
def get_initial(self):
initial = super(CreditDetail, self).get_initial()
initial['request'] = self.request
return initial
def get(self, *args, **kwargs):
request_form = CreditOnlineRequestForm(self.request.GET or None, prefix="request")
class CreditOnlineRequestView(CreateView):
form_class = CreditOnlineRequestForm
model = CreditOnlineRequest
template_name = 'credits/credit_listing.html'
prefix = 'request'
def form_valid(self, form, **kwargs):
credit_request = form.save(commit=False)
credit_request.credit = credit #???
return super(CreditOnlineRequestView, self).form_valid(form)
def form_invalid(self, form):
errors = dict([(k, v[0]) for k, v in form.errors.items()])
return errors
forms
class CreditOnlineRequestForm(forms.ModelForm):
class Meta:
model = CreditOnlineRequest
exclude = ['credit'] #this field must be define
def __init__(self, *args, **kwargs):
super(CreditOnlineRequestForm, self).__init__(*args, **kwargs)
#???
What are the options? I think, either through the cache, or through pulling out the previous page to do, but this is somehow not very humane, as for me. The best option, as for me, is to transfer the credit instance to a hidden form field in the CreditDetail view, but I don’t know how to do it yet.
The problem is that internally the form_valid function is doing the following:
def form_valid(self, form):
"""If the form is valid, save the associated model."""
self.object = form.save()
return super().form_valid(form)
So it does not matter what you're doing in your override that the super will try to save the form directly. You can solve your problem by doing:
def form_valid(self, form, **kwargs):
credit_request = form.save(commit=False)
credit_request.credit = credit
credit_request.save()
return HttpResponseRedirect(self.get_success_url())
urls
url(r'kredit_request/(?P<credit_slug>[-\.\w\d]+)/$', CreditOnlineRequestView.as_view(), name='credit_request'),
html
<form action="{% url 'credit_request' credit.slug %}" method="post">
view
class CreditOnlineRequestView(CreateView):
form_class = CreditOnlineRequestForm
model = CreditOnlineRequest
slug_url_kwarg = 'credit_slug'
prefix = 'request'
def post(self, request, *args, **kwargs):
form = self.get_form()
credit = Credit.objects.get(slug=kwargs.get('credit_slug'))
cache.set('credit_for_request', credit)
if form.is_valid():
return self.form_valid(form)
else:
return self.form_invalid(form)
def form_valid(self, form):
credit_request = form.save(commit=False)
credit = cache.get('credit_for_request')
cache.clear()
credit_request.credit = credit
credit_request.save()
return HttpResponseRedirect(reverse('credit_detail', kwargs={'credit_slug': credit.slug}))
I am using Django 1.11. I am trying to add a value to a hidden field in an inline formset form. I have tried unsuccessfully inserting the hidden field value at various points of def get_context_data and def form_valid. The code I am using is as follows:
views.py
#method_decorator(login_required, name='dispatch')
class DocumentCreate(CreateView):
model = DocumentClient
success_url = reverse_lazy('documents')
form_class = DocumentForm
def get_context_data(self, **kwargs):
data = super(DocumentCreate, self).get_context_data(**kwargs)
if self.request.POST:
data['docform'] = DocumentFormSet(self.request.POST, self.request.FILES)
else:
data['docform'] = DocumentFormSet()
return data
def form_valid(self, form):
context = self.get_context_data()
docform = context['docform']
if docform.is_valid():
self.object = form.save()
docform.instance = self.object
docform.save()
return HttpResponseRedirect('documents')
else:
return self.render_to_response(self.get_context_data(form=form))
forms.py
class DocumentForm(ModelForm):
class Meta:
model = DocumentClient
exclude = ()
widgets = {
'cnum': HiddenInput(),
}
def __init__(self, *args, **kwargs):
super(DocumentForm, self).__init__(*args, **kwargs)
for field in self.fields:
self.fields['cnum'].required = False
class DocumentDetailForm(ModelForm):
class Meta:
model = DocumentDetail
exclude = ()
widgets = {
'document_date': DateInput(),
}
def __init__(self, *args, **kwargs):
super(DocumentDetailForm, self).__init__(*args, **kwargs)
self.fields['document_description'].required = False
DocumentFormSet = inlineformset_factory(DocumentClient, DocumentDetail, form=DocumentDetailForm, extra=10, can_delete=False)
The hidden field 'cnum' is that what I am trying to insert a value for capturing in the model. Is anyone able to provide any guidance on how to acheive this? Any assistance is gratefully appreciated!
In DocumentCreate, have you tried this?
class DocumentCreate(CreateView):
def get_initial(self):
# Get initial value from kwargs (If needed) and save as instance variable.
self.cnum_val = self.kwargs.get('cnum_value')
def form_valid(self, form):
# Insert your desired value to cnum (or you can simply forget get_initial and supply whatever value here)
form.instance.cnum = self.cnum_val
self.object = form.save()
...
self.render_to_response(self.get_context_data(form=form))
form.instance refers to the unsaved model object used by the form
See here also.