How to populate formview with model data when fetched via GET - python

I have the form display info via GET request, currently I assign a new form in get_context_data, is there a better way to do it? And http decorator doesn't work too.
#require_http_methods(['GET',])
class UniversityDetail(SingleObjectMixin, FormView):
model = KBUniversity
form_class = KBUniversityForm
template_name = 'universities/form.html'
def get(self, request, *args, **kwargs):
self.object = self.get_object()
return super(UniversityDetail, self).get(self, request, *args, **kwargs)
def get_context_data(self, **kwargs):
context = super(UniversityDetail, self).get_context_data(**kwargs)
context['form'] = KBUniversityForm(instance=self.object)
context['university_id'] = self.object.pk
return context

Use UpdateView instead of FormView with SingleObjectMixin. In 99% of the cases you should not override the get method.

Related

Inspect response when i use a view Class-based views - Django

I need to inspect the response for know the params of pagination. I try used Pdb but the application stops. I think to overwrite some method like def dispatch(self, request, *args, **kwargs): but i don't know if the best way, someone could guide me ?
class PostsFeedView(LoginRequiredMixin, ListView):
template_name = 'posts.html'
model = Post
ordering = ('-created',)
paginate_by = 10
context_object_name = 'posts'
# import pdb; pdb.set_trace()
You can override get:
class PostsFeedView(LoginRequiredMixin, ListView):
...
def get(self, *args, **kwargs):
response = super().get(*args, **kwargs)
import pdb; pdb.set_trace()
return response

Problem with mixing Detail View and Form Mixin django

I am trying to create a comment system for the blog portion of my app with Django. I have attempted to mix my detail view with the form mixin and I'm struggling a bit. When the form is submitted, it doesn't save and no error is present. If any of you can help I would greatly appreciate it.
Here is my View
class DetailPostView(FormMixin, DetailView):
model = Post
template_name = "blog/post_detail.html"
context_object_name = "posts"
form_class = CommentForm
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context["form"] = CommentForm
return context
def post(self, request, *args, **kwargs):
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 get_success_url(self):
return reverse("post-detail", kwargs={"pk": self.object.pk})
The model
class Comment(models.Model):
comment = models.ForeignKey(Post, on_delete=models.CASCADE)
title = models.CharField(max_length=200)
content = models.TextField()
author = models.CharField(max_length=50)
created_on = models.DateTimeField(auto_now_add=True)
class Meta:
ordering = ["-created_on"]
def __str__(self):
return self.title
The reason that this happens is because you construct a new form that you pass to the context data, as a result, it will not render any errors, since you construct a form without validating the request data and render that form, you thus do not display the form that rejected the data in the first place.
But you do not need to do that. Django's FormMixin [Django-doc] already takes care of that. You thus should not override the .get_context_data(…) method [Django-doc].
Another problem is that you did not save your form, you can override a the form_valid method, or you can inherit from ModelFormMixin [Django-doc].
Finally you better first create the form, and then assign self.object, otherwise it will pass this as an instance to the form:
from django.views.generic.edit import ModelFormMixin
class DetailPostView(ModelFormMixin, DetailView):
model = Post
template_name = 'blog/post_detail.html'
context_object_name = 'posts'
form_class = CommentForm
# no get_context_data override
def post(self, request, *args, **kwargs):
# first construct the form to avoid using it as instance
form = self.get_form()
self.object = self.get_object()
if form.is_valid():
return self.form_valid(form)
else:
return self.form_invalid(form)
def get_success_url(self):
return reverse('post-detail', kwargs={'pk': self.object.pk})

django-filter according to the logged user

I am using the django_filters to filtering the information. However, it is showing the data of all regitered user of my system. I need show only the data of logged user.
Follow the files:
filters.py
import django_filters
from apps.requisitos.models import Requisito
class RequisitoFilter(django_filters.FilterSet):
class Meta:
model = Requisito
fields = ['nomeRequisito', 'projeto']
views.py
class RequisitoList(ListView):
paginate_by = 10
model = Requisito
def get_queryset(self):
usuarioLogado = self.request.user
return Requisito.objects.filter(user=usuarioLogado)
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['filter'] = RequisitoFilter(self.request.GET, queryset=self.queryset)
return context
the loop for of html page
{% for requisito in filter.qs %}
thank you very much
You need to pass the result of get_queryset to the queryset parameter:
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['filter'] = RequisitoFilter(
self.request.GET,
queryset=self.get_queryset()
)
return context
Note: You might want to consider using a FilterView [GitHub],
this view implements most of the ListView [Django-doc],
and encapsulates logic to use a filter_class.

get_context_data function doesn't return the 'form'

I have the following Django code (django version 1.11) of a form:
class MyDocumentUpdateView(UpdateView):
""" We extend UpdateView as we need to pass self.object in context as resource """
model = MyDocument
def get_context_data(self, **context):
context[self.context_object_name] = self.object
context['resource'] = self.object
#context['form'] = self.get_form() <-- if I uncomment this it works
return context
class DocumentUpdateView(MyDocumentUpdateView):
form_class = MyDocumentForm
def form_valid(self, form):
doc = form.save(commit=False)
doc.owner = self.request.user
updateMyDocument(doc, form)
return HttpResponseRedirect(
reverse(
'mydocs_detail',
args=(
self.object.slug,
)))
When I run this code, I get an error:
'str' object has no attribute 'fields'
I realized that this error related with the return of the context variable. For some reason the context dictionary does not include the 'form'. As indicated in the documentation:
get_context_data(**kwargs)¶
Calls get_form() and adds the result to the context data with the name ‘form’.
But in my case the context dictionary does not include the 'form'.
If I use the get_form() def, then everything works:
context['form'] = self.get_form()
But this seems like an overkill to me and I want to dig deeper on why this doesn't work.
Then I have noticed that in my case I use:
def get_context_data(self, **context):
instead of:
def get_context_data(self, **kwargs):
So I defined the context as following:
context = super(DocumentUpdateView, self).get_context_data(**kwargs)
But then I get the following error:
maximum recursion depth exceeded
So I defined the context as following:
context = super(DocumentUpdateView, self).get_context_data(**kwargs)
You used the wrong class, since you call this at the MyDocumentUpdateView level, it should be super(MyDocumentUpdateView, self).get_context_data(**kwargs). So you should implement this as:
class MyDocumentUpdateView(UpdateView):
""" We extend UpdateView as we need to pass self.object in context as resource """
model = MyDocument
def get_context_data(self, **kwargs):
context = super(MyDocumentUpdateView, self).get_context_data(**kwargs)
context['resource'] = self.object
return context
That being said, the self.object is already added to the context, by default as the name of the model (so here that would be mydocument, and you can specify one on your own by specifying a value for the context_object_name attribute, like:
class MyDocumentUpdateView(UpdateView):
""" We extend UpdateView as we need to pass self.object in context as resource """
model = MyDocument
context_object_name = 'resource'
in that case, the context of course has no longer the object stored under its model name (or any other name).

How To Access The Request Object in Django's GenericStackedInline Admin

Using GenericStackedInline in Django 1.9 (Python 3.4) I want to access the request object before saving my model in the Django Admin.
When using MediaItemAdmin I can intercept the save function before obj.save() is run, as in this example:
admin.py
class StuffAdmin(admin.ModelAdmin):
def save_model(self, request, obj, form, change):
# Do some stuff here like obj.user = request.user before saving.
obj.save()
However, the same behaviour or 'hook' isn't available using a GenericStackedInline. It appears to call the model save method directly:
admin.py
class StuffAdmin(GenericStackedInline):
model = StuffModel
def save_model(self, request, obj, form, change):
print("I'm never run :(")
obj.save()
As I understand GenericStackedInline inherits from a form so I have also tried using a form and overriding that as in this example:
admin.py
class StuffAdmin(GenericStackedInline):
model = StuffModel
form = StuffForm
class StuffForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(StuffForm, self).__init__(*args, **kwargs)
def save_model(self, request, obj, form, change):
print("Still not run!(")
obj.save()
def save_form(self, request, obj, form, change):
print("Work already!")
obj.save()
I have searched stackoverflow, but most are unanswered, as seen here accessing request object within a django admin inline model or say use init to do something like self.request = kwargs.pop('request') however, request is never passed here, right?
Anyhow, any idea how I can call the request object and update my instance before the model save() is called?
The method that saves the "inlines" is part of ModelAdmin, not InlineModelAdmin.
class BarInline(GenericStackedInline):
model = Bar
class FooModelAdmin(ModelAdmin):
model = Foo
inlines = [BarInline]
def save_formset(self, request, form, formset, change):
"""
`form` is the base Foo form
`formset` is the ("Bar") formset to save
`change` is True if you are editing an existing Foo,
False if you are creating a new Foo
"""
if formset_matches_your_inline_or_some_requirement(formset):
do_something_with(request)
super().save_formset(request, form, formset, change)
If you want to check whether the formset is the BarInline's formset, you can do something like this:
class BarInline(GenericStackedInline):
model = Bar
def get_formset(self, *args, **kwargs):
formset = super().get_formset(*args, **kwargs)
formset.i_come_from_bar_inline = True
return formset
class FooModelAdmin(ModelAdmin):
model = Foo
inlines = [BarInline]
def save_formset(self, request, form, formset, change):
if getattr(formset, 'i_come_from_bar_inline', False):
do_something_with(request)
super().save_formset(request, form, formset, change)
Or even better, make it generic:
class BarInline(GenericStackedInline):
model = Bar
def pre_save_formset(self, request, form, model_admin, change):
"""Do something here with `request`."""
class FooModelAdmin(ModelAdmin):
model = Foo
inlines = [BarInline]
def save_formset(self, request, form, formset, change):
if hasattr(formset, 'pre_save_formset'):
formset.pre_save_formset(request, form, self, change)
super().save_formset(request, form, formset, change)
if hasattr(formset, 'post_save_formset'):
formset.post_save_formset(request, form, self, change)
If you need to do something with the request before each form save rather than before each formset, you will have to use your own Form and FormSet propagate the request through the formset to the form:
from django.forms import ModelForm
from django.forms.models import BaseInlineFormSet
class BarForm(ModelForm):
model = Bar
def __init__(self, *args, **kwargs):
request = kwargs.pop('request', None)
super().__init__(*args, **kwargs)
self.request = request
def save(self, commit=True):
print(self.request)
print(self.instance)
obj = super().save(False) # Get object but don't save it
do_something_with(self.request, obj)
if commit:
obj.save()
self.save_m2m()
return obj
class BarFormSet(BaseInlineFormSet):
#property
def request(self):
return self._request
#request.setter
def request(self, request):
self._request = request
for form in self.forms:
form.request = request
class BarInline(GenericStackedInline):
codel = Bar
form = BarForm
formset = BarFormSet
class FooModelAdmin(ModelAdmin):
inlines = [BarInline]
def _create_formsets(self, request, obj, change):
formsets, inline_instances = super()._create_formsets(request, obj, change)
for formset in formsets:
formset.request = request
return formsets, inline_instances
According to you usecase, the save method might also simply look like something like this:
class BarForm(ModelForm):
model = Bar
def save(self, commit=True):
do_something_with(self.request, self.instance)
return super().save(commit) # Get object but don't save it
Admin classes don't inherit from forms; they include forms. And ModelForms don't have either save_model or save_form methods, they just have a save method. It's perfectly possible to override that method, but it doesn't accept request; you'd need to also override __init__ to accept that argument and pass it in from the modeladmin's get_form_kwargs method.

Categories