I have two views, PostCreateView and PostUpdateView. They both route through the same html template, post_form.html. I want to create a Copy button that only appears if I am accessing a record through PostUpdateView. Pressing the Copy button will create a new record pre-filled with all the data from record that I was just on.
PostCreateView code:
class PostCreateView(LoginRequiredMixin, FormView):
template_name = 'trucking/post_form.html'
form_class = RecordForm
def form_valid(self, form):
form.instance.author = self.request.user
obj = form.save(commit=False)
obj.save()
messages.success(self.request, f'RECORD: {obj.container_no} was saved')
return super().form_valid(form)
def get_success_url(self):
if self.request.POST.get('Save_Exit'):
return reverse('close')
elif self.request.POST.get('Save'):
return reverse('post-create')
else:
return reverse('database')
PostUpdateView code:
class PostUpdateView(LoginRequiredMixin, UpdateView):
form_class = RecordForm
model = Post
def get_context_data(self, **kwargs):
data = super().get_context_data(**kwargs)
history = Post.objects.filter(id = self.object.id).first().history.all().reverse()
data['history'] = get_status_changes(history)
return data
# checks to make sure the user is logged in
def form_valid(self, form):
form.instance.author = self.request.user
obj = form.save(commit=False)
messages.success(self.request, f'RECORD: {obj.container_no} was updated')
return super().form_valid(form)
def get_success_url(self):
if self.request.POST.get('Save_Exit'):
return reverse('close')
# return render(self.request, 'trucking/email_close.html')
elif self.request.POST.get('Save'):
return reverse('post-update', kwargs={'pk': self.object.id})
else:
return reverse('database')
Based on this post I tried creating a view like so:
def copy(request):
post = Post.objects.get(pk = request.users.post.id)
my_form = RecordForm(instance = post)
return render(request, 'trucking/post_form.html', {'form': my_form})
However, I get an error saying 'WSGIRequest' object has no attribute 'users'. I think that's not the only issue with this approach.
Should it be a part of PostUpdateView, since it takes the form information from an existing record? Should it be part of PostCreateView since you take the preexisting info and fill out a new form with it? Should it be a new view altogether? Any help is appreciated
Related
I've got one master detail relationship beteen Article and Section, and wish to allow a user to add sections on the same page before creating the Article.
It seems to work, but I'm also very new to Django, and wonder if I'm doing it right. There's a lot going on here, and I wonder if I'm overriding methods correctly, for example, and when I do, is the content correct?
A gold-standard way of doing it would be very appreciated!
class ArticleCreateView(LoginRequiredMixin,CreateView):
template_name = 'articles/article_add.html'
model = Article
form_class = ArticleForm
success_url = reverse_lazy('index')
SectionFormSet = inlineformset_factory(Article, Section, extra=2, can_delete=False, fields=('content',))
def get(self, request, *args, **kwargs):
print('get called on article create view')
self.object = None #what does this achieve?
return self.render_to_response(
self.get_context_data(form=self.get_form(),
section_form=self.SectionFormSet(),
))
def post(self, request, *args, **kwargs):
print('post called on article create view')
self.object = None
form = self.get_form()
section_form = self.SectionFormSet(self.request.POST)
if (form.is_valid() and section_form.is_valid()):
return self.form_valid(form, section_form)
else:
return self.form_invalid(form, section_form)
def form_valid(self, form, section_form):
form.instance.author = self.request.user
if section_form.is_valid():
print('section form is valid')
self.object = form.save()
section_form.instance = self.object
section_form.save()
return HttpResponseRedirect(self.success_url)
#return super().form_valid(form)
'''
return self.render_to_response( self.get_context_data(form=form,
section_form=section_form,
))
'''
def form_invalid(self, form, article_form):
return self.render_to_response(
self.get_context_data(form=form,
article_form=article_form))
I don't know if this is 100% correct, but it seems better than my previous code :
class ArticleCreateView(LoginRequiredMixin,CreateView):
template_name = 'articles/article_add.html'
form_class = ArticleForm
SectionFormSet = inlineformset_factory(Article, Section, extra=2, can_delete=False, fields=('content',))
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
if self.request.POST:
context['section_form'] = self.SectionFormSet(self.request.POST,instance=self.object)
else:
context['section_form'] = self.SectionFormSet(instance=self.object)
return context
def save_sections(self):
try:
context = self.get_context_data()
section_form = context['section_form']
if section_form.is_valid():
#section_form.instance = self.object
section_form.save()
except Exception as e:
print('failed to save section: ' + str(e))
def form_valid(self, form):
form.instance.author = self.request.user
response = super().form_valid(form) #save article form
self.save_sections()
return response
def get_success_url(self):
return reverse_lazy('index')
here's the update view (for completion), pretty much identical except it gets the instance of the article in the formset factory
class ArticleUpdateView(LoginRequiredMixin,UserPassesTestMixin,UpdateView):
template_name = 'articles/ArticleUpdate.html'
form_class = ArticleForm
model = Article
SectionFormSet = inlineformset_factory(Article, Section, extra=0, can_delete=False, fields=('content',))
def test_func(self):
article = self.get_object()
if self.request.user == article.author or self.request.user.is_superuser :
return True
else:
return False
def get_context_data(self, **kwargs):
print('get context data called update view')
context = super().get_context_data(**kwargs)
if self.request.POST:
context['section_form'] = self.SectionFormSet(self.request.POST,instance=self.object)
else:
context['section_form'] = self.SectionFormSet(instance=self.object)
return context
def save_sections(self):
print('save sections called update view')
try:
context = self.get_context_data()
section_form = context['section_form']
if section_form.is_valid():
# section_form.instance = self.object #if im passing instance in the factory, do I need it here to?
section_form.save()
except Exception as e:
print('failed to save section: ' + str(e))
def form_valid(self, form):
print('form valid called update view')
form.instance.author = self.request.user
response = super().form_valid(form) #save article form
self.save_sections()
return response
def get_success_url(self):
return reverse_lazy('index')
I hope you're well. Just one question that's been bothering me a lot lately...
I got the issue only when I update my form. The update works after refreshing the page.
views.py in user folder
#update detail
#method_decorator(login_required(login_url='/earlycooker/login/'),name="dispatch")
class UserProfileUpdateView(UpdateView):
model = UserProfile
template_name = 'profile-update.html'
form_class = UserProfileForm
success_message = "Profile updated"
def form_valid(self, form):
form.instance.user = self.request.user
form.save()
return super(UserProfileUpdateView, self).form_valid(form)
def get_success_url(self):
return reverse('update_profile',kwargs={'slug':self.object.slug})
def get(self,request,*args,**kwargs):
self.object = self.get_object()
if self.object.user != request.user:
return HttpResponseRedirect('/')
return super(UserProfileUpdateView, self).get(request,*args,**kwargs)
urls.py in user folder
path('details/<slug:slug>/', UserProfileUpdateView.as_view(), name="update_profile"),
profile-update.html in user folder
{% url 'user:update_profile' slug=user.userprofile.slug %}
From this 'user:update_profile' ,The application name (app_name) of the update_profile url is user.
#method_decorator(login_required(login_url='/earlycooker/login/'),name="dispatch")
class UserProfileUpdateView(UpdateView):
model = UserProfile
template_name = 'profile-update.html'
form_class = UserProfileForm
success_message = "Profile updated"
def form_valid(self, form):
form.instance.user = self.request.user
form.save()
return super(UserProfileUpdateView, self).form_valid(form)
def get_success_url(self):
return reverse('user:update_profile',kwargs={'slug':self.object.slug})
def get(self,request,*args,**kwargs):
self.object = self.get_object()
if self.object.user != request.user:
return HttpResponseRedirect('/')
return super(UserProfileUpdateView, self).get(request,*args,**kwargs)
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 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)
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??