Django - overload post on UpdateView so it auto completes some fields - django

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)

Related

ignore fields on update view

I have a very large form that I have an update view for. The issue is when the users submits an update it says some fields are required such as author and post date. I don't want users to change these fields. The fields are manually rendered
How can I ignore these fields in the update view.
I have tried to set the requirements to false
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['author'].required = False
self.fields['date_posted'].required = False
But this throws a null value in column "author" of relation "blog_post" violates not-null constraint
Alot of posts said to add null=True but these fields cannot be null
view:
class PostUpdateView(LoginRequiredMixin, UserPassesTestMixin, UpdateView):
model = Post
form_class = PostFormUpdate
def form_valid(self, form):
form.instance.author = self.request.user
return super().form_valid(form)
def test_func(self):
post = self.get_object()
if self.request.user.id == post.author_id:
return True
return False
form:
class PostFormUpdate(ModelForm):
class Meta:
model = Post
fields = '__all__'
The easiest way to achieve it is just create a new Form to the edit action as following:
class PostFormUpdate2(ModelForm):
class Meta:
model = Post
exclude = ('author','date_posted')
Then you will not have that data in the form as following:
class PostUpdateView(LoginRequiredMixin, UserPassesTestMixin, UpdateView):
model = Post
form_class = PostFormUpdate2
def form_valid(self, form):
form.instance.author = self.request.user
return super().form_valid(form)
def test_func(self):
post = self.get_object()
if self.request.user.id == post.author_id:
return True
return False
This is the idea, try to test it and if it gives you an error let me know in the comments.
you can set blank=True either in model class which will be implemented on all http methods or using the above technique you used
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['author'].blank = True
self.fields['date_posted'].blank = True
blank=True means you are not requiring any values from forms for those fields...
This makes it so the user cannot change the form field but it is still displayed
widgets = {
'date_posted': forms.TextInput(attrs={'readonly':'readonly'}), }
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['date_posted'].required = False

Django FormView: Add if record does not exist and update if it does exist

I created a FormView and it works fine if the user executed the process the first time. However when it is executed the second time I get an error that the record already exist. This is expected as the user in the model is unique. How can I overcome this problem so that the current record is overwritten by the form.save if the record already exist.
models.py
class ttemp_selection(models.Model):
select_account = models.ForeignKey(tledger_account, on_delete=models.CASCADE)
date_from = models.DateField(default=datetime.today)
date_to = models.DateField(default=datetime.today)
user = models.ForeignKey(custom_user, on_delete=models.CASCADE, unique=True)
def __str__(self):
return self.select_account
forms.py
class Meta:
model = ttemp_selection
fields = ['select_account', 'date_from', 'date_to', 'user']
def __init__(self, *args, **kwargs):
self.request = kwargs.pop('request')
super(SelectAccountForm, self).__init__(*args, **kwargs)
user = self.request.user
current_company = user.current_company
self.fields['select_account'].queryset = tledger_account.objects.filter(
company=current_company, gl_category='Cash and Bank')
view.py
class sasView(FormView):
template_name = 'cashflow/select_account.html'
form_class = SelectAccountForm
success_url = 'home'
def form_valid(self, form):
form.save()
return super().form_valid(form)
def get_form_kwargs(self):
kwargs = super(sasView, self).get_form_kwargs()
kwargs['request'] = self.request
return kwargs
I can determine the record by using ttemp_selection.objects.get(user=request.user)
I know I can make use of the UpdateView class but that will create a problem when the record does not exist. It will also add an extra step that is unnecessary.
Assistance will be appreciated.
You can work with a CreateView, and slightly alter the behavior to specify a self.object if that exists:
from django.contrib.auth.mixins import LoginRequiredMixin
class sasView(LoginRequiredMixin, CreateView):
template_name = 'cashflow/select_account.html'
form_class = SelectAccountForm
success_url = 'home'
def get_form(self, *args, **kwargs):
self.object = ttemp_selection.objects.filter(
user=self.request.user
).first()
return super().get_form(*args, **kwargs)
def form_valid(self, form):
form.instance.user = self.request.user
return super().form_valid(form)
def get_form_kwargs(self):
kwargs = super(sasView, self).get_form_kwargs()
kwargs['request'] = self.request
return kwargs
It however makes no sense to include the user as field, since - if I understand it correctly - you use the logged in user. By including it, you make it possible that a person forges a POST request, and thus changes the account of a different user. You should omit this filed:
class SelectAccountForm(forms.ModelForm):
class Meta:
model = ttemp_selection
# no user ↓
fields = ['select_account', 'date_from', 'date_to']
# …
Note: You can limit views to a class-based view to authenticated users with the
LoginRequiredMixin mixin [Django-doc].

Django inline formset validation

I have the following models:
class CaseForm(ModelForm):
class Meta:
model = Case
fields = '__all__'
class ClientForm(ModelForm):
class Meta:
model = Client
fields = '_all__'
CaseClientFormset = inlineformset_factory(Case, Client, form=ClientForm,
extra=0, max_num=2, min_num=1,
validate_max=True,
validate_min=True)
When I fill in the top part of the form (caseform) it saves correctly. When I fill in the caseform and a clientform it saves correctly.
If I fill in the caseform but partially fill in the clientform no validation appears to take place, and a case is saved and the client information goes missing and is never saved.
class CaseCreateView(LoginRequiredMixin, AdviserExistenceMixin,
CreateView):
model = Case
form_class = CaseForm
def form_valid(self, form):
context = self.get_context_data()
clients = context['clients']
self.object = form.save()
if clients.is_valid():
clients.instance = self.object
clients.save()
return super(CaseCreateView, self).form_valid(form)
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
if self.request.POST:
context['clients'] = CaseClientFormset(self.request.POST)
else:
context['clients'] = CaseClientFormset()
context['navbar'] = str(self.model.__name__).lower()
return context
The other issue I have is that despite specifying min_num=1 and validate_min=True, I appear to be able to save a case without a clientform being filled in.
Any help would be appreciated.
Fixed replacing def form_valid with:
def form_valid(self, form):
context = self.get_context_data()
clients = context['clients']
if clients.is_valid():
self.object = form.save()
clients.instance = self.object
clients.save()
return super(CaseUpdateView, self).form_valid(form)
else:
return super(CaseUpdateView, self).form_invalid(form)

Django add value to hidden field inline formset

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.

NOT NULL constraint failed when save ModelForm

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.