Test form_valid() in Createview - django

I am trying to test one of my CreateViews. The whole view looks like this:
class BookCreate(CreateView):
model = Book
form_class = BookForm
template_name = 'base/components/form.html'
def form_valid(self, form):
form.instance.user = self.request.user
form.save()
return HttpResponse(
status=204,
headers={
'HX-Trigger': json.dumps({
"bookListChanged": None,
})
})
The part of the view that needs testing according to coverage.py is this part:
form.instance.user = self.request.user
form.save()
return HttpResponse(
My test right now looks like this:
class TestBookViews(TestCase):
def setUp(self) -> None:
self.user = User.objects.create_user(email='test#gmail.com', password='teSTpassword123')
self.client.login(email='test#gmail.com', password='teSTpassword123')
return super().setUp()
def test_book_create(self):
response = self.client.post(reverse('base:book-create'),
{
'name': 'TestBook',
'user': self.user,
'publishing_date': '2022-08-17',
})
self.assertEqual(response.status_code, 204)
But this test does not make it covered. What I am missing?

Related

How Do I Convert This Function Based View To An UpdateView?

I am trying to convert a function based view to a class based view. I've done it with the CreateView but the UpdateView is giving me grief. It won't take my update. I can get the view to take my update, but it doesn't save it.
Here's my function based view:
def update_task_update_view(request, pk):
task = Task.objects.get(id=pk)
form = TaskForm(request.POST or None, instance=task)
if request.method == "POST":
if form.is_valid():
form.save()
return redirect("MyTasks:task_detail", pk=task.id)
context = {
"form": form,
"task": task
}
return render(request, "partials/task_form.html", context)
And here was my attempt at a Class Based View.
class UpdateTaskUpdateView(LoginRequiredMixin,UpdateView):
model = Task
form_class = TaskForm
template_name = 'partials/task_form.html'
def post(self, request, *args, **kwargs):
form_class = self.get_form_class()
form = self.get_form(form_class)
if form.is_valid():
task = form.save()
task.save()
return redirect("MyTasks:task_detail", pk=task.id)
else:
return render(request, "partials/task_form.html", {
"form":form
})
This function based view is working fine, no issues with it.
Thanks to an assist from FB...Travis Tucker....
I did this instead and it seems to be working fine...
class UpdateTaskUpdateView(LoginRequiredMixin,UpdateView):
model = Task
form_class = TaskForm
template_name = 'partials/task_form.html'
def form_valid(self, form):
form_class = self.get_form_class()
form = self.get_form(form_class)
if form.is_valid():
task = form.save()
task.save()
return redirect("MyTasks:task_detail", pk=task.id)
else:
return render(request, "partials/task_form.html", {
"form":form
})

Is this the correct way to use a CreateView in Django with an inlineformset_factory?

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')

Django class UpdateView

I have this view and I use this code
class post_Wells(UserPassesTestMixin, LoginRequiredMixin, UpdateView):
model = Wellinfo
template_name = 'Home/WELLINFO/detailw2.html'
form_class = NewWells
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== post.author:
return True
else:
return True
everything works good, but I want to send other data from this class
for example I want to render this text:
context ={
'Mytext': 'This well is Open'}
how to do it inside this class?
You can do that by overriding a method for the context, get_context_data.
You should bookmark this site; http://ccbv.co.uk/
Docs for this specific view are here; http://ccbv.co.uk/projects/Django/3.0/django.views.generic.edit/UpdateView/#get_context_data
And taking into account good practice, your class would become;
class PostWells(UserPassesTestMixin, LoginRequiredMixin, UpdateView):
form_class = NewWells
model = Wellinfo
template_name = 'Home/WELLINFO/detailw2.html'
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== post.author:
return True
else:
return True
def get_context_data(self, **kwargs):
"""Insert your data into the context dict."""
context = super().get_context_data(**kwargs)
context['Mytext'] = 'This well is Open'
return context
You can try like this using the get_context_data().
class post_Wells(UserPassesTestMixin, LoginRequiredMixin, UpdateView):
model = Wellinfo
template_name = 'Home/WELLINFO/detailw2.html'
form_class = NewWells
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== post.author:
return True
else:
return True
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['Mytext'] = 'This well is Open'
return context

Reverse for 'update_profile' not found. 'update_profile' is not a valid view function or pattern name

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)

How to get object at CreatView

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}))