Python/Django - not access to linked model in a generic view - python

I'm newbie in Python and Django
I've 3 models linked: patient -> visit -> prescription
I want to override get_context_data in a detailView to have access to all prescription related to patient
visit to patient related_name = 'visits'
prescription to visit related_name = 'prescriptions'
but I have an error:
PatientFile object has no attribute 'visits'
I look what is inside self:
patient.views.PatientFile object at 0x04772EB0
I don't understand
self is my patient instance so I should have access to all visits with attribute 'visits'?
class PatientFile(DetailView):
model = Patient
context_object_name = "patient"
template_name = "patient/file.html"
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['prescriptions'] = []
print('self : ', self)
for visit in self.visits:
context['prescriptions'].append(visits.prescriptions)
return context

The self in your DetailView is the PatientFile view object, not the Patient object.
You can however access the Patient object with self.object [Django-doc]:
class PatientFile(DetailView):
model = Patient
context_object_name = 'patient'
template_name = 'patient/file.html'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['prescriptions'] = prescriptions = []
for visit in self.object.visits.all():
prescriptions.extend(visit.prescriptions.all())
return context
Note that in order to iterate over a relation, you should use .all(), not just self.visits.

Related

Using inlineformset_factory with different querysets in CreateView

I am trying to use inlineformset_factory to create instances of the same model.
models.py
class Skill(models.Model):
employee = models.ForeignKey(
Employee, on_delete=models.CASCADE, related_name="employee_skills")
technology = models.ForeignKey(Technology, on_delete=models.CASCADE)
year = models.CharField('common year using amount ', max_length=4)
last_year = models.CharField('Last year of technology using ', max_length=4)
level = models.CharField("experience level", max_length=64, choices=LEVELS)
class Techgroup(models.Model):
""" Group of technology """
name = models.CharField('group_name', max_length=32, unique=True)
class Technology(models.Model):
"""Technologies."""
name = models.CharField('technology name', max_length=32, unique=True)
group = models.ForeignKey(Techgroup, on_delete=models.CASCADE, related_name="group")
In the Administrator pane I created 2 instances of the Techgroup model:
- Framework
- Programming language
All Skill models belong to one of two groups. On the front I display 2 forms, one containing queryset with instances belonging to the Framework, the other with instances belonging to the Programming language.
I divide Querysets using ModelsForm:
forms.py
class SkillBaseCreateForm(forms.ModelForm):
YEAR_CHOICES = [(r, r) for r in range(1, 11)]
LAST_YEAR_CHOICES = [(r, r) for r in range(2015, datetime.datetime.now().year + 1)]
year = forms.CharField(
widget=forms.Select(choices=YEAR_CHOICES),
)
last_year = forms.CharField(widget=forms.Select(choices=LAST_YEAR_CHOICES))
class Meta:
model = Skill
fields = ['technology', 'level', 'last_year', 'year']
class SkillCreatePLanguageForm(SkillBaseCreateForm):
def __init__(self, *args, **kwargs):
super(SkillCreatePLanguageForm, self).__init__(*args, **kwargs)
self.fields['technology'].queryset = Technology.objects.filter(group__name="Programming language")
class SkillCreateFrameworkForm(SkillBaseCreateForm):
def __init__(self, *args, **kwargs):
super(SkillCreateFrameworkForm, self).__init__(*args, **kwargs)
self.fields['technology'].queryset = Technology.objects.filter(group__name="Framework")
SkillFrameworkFormSet = inlineformset_factory(Employee, Skill, form=SkillCreateFrameworkForm, extra=1, can_delete=False)
SkillPLanguageFormSet = inlineformset_factory(Employee, Skill, form=SkillCreatePLanguageForm, extra=1, can_delete=False)
views.py
class SkillTestCreateView(AuthorizedMixin, CreateView):
"""
Create new skill instances
"""
template_name = 'edit.html'
model = Employee
form_class = EmployeeEditForm
def get(self, *args, **kwargs):
"""
Handles GET requests and instantiates blank versions of the form
and its inline formsets.
"""
self.object = Employee.objects.get(pk=self.kwargs['pk'])
form_class = self.get_form_class()
form = self.get_form(form_class)
form_framework = SkillFrameworkFormSet()
form_language = SkillPLanguageFormSet()
return self.render_to_response(
self.get_context_data(form=form,
form_framework=form_framework,
form_language=form_language))
def post(self, request, *args, **kwargs):
"""
Handles POST requests, instantiating a form instance and its inline
formsets with the passed POST variables and then checking them for
validity.
"""
self.object = Employee.objects.get(pk=self.kwargs['pk'])
form_class = self.get_form_class()
form = self.get_form(form_class)
form_framework = SkillFrameworkFormSet(self.request.POST)
form_language = SkillPLanguageFormSet(self.request.POST)
if (form.is_valid() and form_framework.is_valid() and
form_language.is_valid()):
return self.form_valid(form, form_framework, form_language)
else:
return self.form_invalid(form, form_framework, form_language)
def form_valid(self, form, form_framework, form_language):
"""
Called if all forms are valid. Creates a Employee instance along with
associated models and then redirects to a
success page.
"""
self.object = form.save()
form_framework.instance = self.object
form_framework.save()
form_language.instance = self.object
form_language.save()
return HttpResponseRedirect(reverse_lazy('profile', args=[self.kwargs['pk']]))
def form_invalid(self, form, form_framework, form_language):
"""
Called if a form is invalid. Re-renders the context data with the
data-filled forms and errors.
"""
return self.render_to_response(
self.get_context_data(form=form,
form_framework=form_framework,
form_language=form_language,
))
The problem is that I always get an error message when I submit a form:
Select a valid choice. That choice is not one of the available choices.
The problem is that when I submit a form, I always get an error message in the technology field of the queryset which is displayed first in the template.
That is, if the template
{form_framework}}
{form_language}
a mistake on
queryset = Technology.objects.filter(group__name="Framework"
if
{form_language}
{form_framework}}
a mistake on
queryset = Technology.objects.filter(group__name="Programming language"
If I leave only one form in views.py, everything starts to work.
I've been trying to figure it out for 2 days and I think I'm at a dead end. Need help!

'QuerySet' object does not support item assignment

class PostDetailView(DetailView):
model = Post
template_name = 'detail.html'
def get_context_data(self, **kwargs):
context = super(PostDetailView, self).get_context_data(**kwargs)
instance = Post.objects.get(pk=self.kwargs.get('pk'))
user = instance.post_user
context['comments'] = Comment.objects.filter(comment_post=instance.pk)
context['comments']['profile'] = Profile.objects.get(user=user)
return context
This is my view so far. When i use that code i get this error 'QuerySet' object does not support item assignment. How do i attach the line below correctly?
context['comments']['profile'] = Profile.objects.get(user=user)
The problems is that value of context['comments'] is not a dictionary but QuerySet object.
So you can't do:
context['comments']['profile'] = Profile.objects.get(user=user).
Maybe you can add relation with Profile model right to the Comment model, like:
class Comment(models.Model):
profile = models.ForeignKey(Profile, ...)
...
So then you can have access to the value of Profile who left a comment.

How can I return regular response in override get method, Django

I try to use class based views in Django. And I have such problem: I define a base class for a blog (BlogBaseView) and two other classes, that inherit it.
And in the second class(BlogIndexView) I want to make the search by get request, so I have override get method. It works, but if I don't make get request, it returns HttpResponse, however I want to return usual context (which BlogIndexView retunes without override get method).
What can I do?
class BlogBaseView(View):
def get_context_data(self, **kwargs):
context = super(BlogBaseView, self).get_context_data(**kwargs)
blog_categories = []
categories = BlogCategory.objects.all()
for category in categories:
blog_categories.append(tuple([category, category.get_number_of_category_items]))
context['name_page'] = 'blog'
context['tags'] = Tag.objects.all()
context['blog_categories'] = blog_categories
return context
class BlogIndexView(BlogBaseView, ListView):
queryset = Post.objects.all().order_by('-date_create')
template_name = 'index_blog.html'
context_object_name = 'posts'
def get(self, request):
if request.GET.get('tag'):
context = {
'posts' : Post.objects.filter(tags__name__in=[request.GET.get('tag')])
}
return render(request, self.template_name, context)
return HttpResponse('result')
class BlogFullPostView(BlogBaseView, DetailView):
model = Post
template_name = 'full_post.html'
pk_url_kwarg = 'post_id'
context_object_name = 'post'
Thanks!
ListView class also has a get_context_data method, so you should override that instead of get method. Using super you'll get access to BlogBaseView.get_context_data and then you can extended the result.
Here's how:
class BlogIndexView(BlogBaseView, ListView):
queryset = Post.objects.all().order_by('-date_create')
template_name = 'index_blog.html'
context_object_name = 'posts'
def get_context_data(self, **kwargs):
# use the (super) force Luke
context = super(BlogIndexView, self).get_context_data(**kwargs)
if self.request.GET.get('tag'):
context['posts'] = Post.objects.filter(tags__name__in=[self.request.GET.get('tag')])
return context
If you are overriding ListView then it's not a good idea to override the get method, as you will lose a lot of the ListView functionality.
In this case, it would be a better idea to override get_queryset, and do the search there.
def get_queryset(self):
queryset = super(BlogIndexView, self). get_queryset()
if request.GET.get('tag'):
queryset = queryset.filter(tags__name=request.GET['tag'])
return queryset

How do I write Django related queries using DetailView

How do I write a DetailView in Django, that has other related Queries?
Here is my example:
class DistributionDetailView(DetailView):
model = Distribution
template_name = 'projects/view_distribution.html'
def get_context_data(self, **kwargs):
context = super(DistributionDetailView,
self).get_context_data(**kwargs)
context['weekly_attendance'] = WeeklyAttendance.objects.filter(
distibution_name=Distribution)
context['sales_data'] = SalesData.objects.filter(
distibution_name=Distribution)
context['theatrical_release'] = TheatricalRelease.objects.filter(
distibution_name=Distribution)
return context
Distribution is the model class, you can't use it inside filter(). In the get_context_data method, you can access the current object with self.object, for example:
context['weekly_attendance'] = WeeklyAttendance.objects.filter(
distibution_name=self.object)
You can access the object with self.object in most generic views:
class DistributionDetailView(DetailView):
model = Distribution
template_name = 'projects/view_distribution.html'
def get_context_data(self, **kwargs):
context = super(DistributionDetailView,
self).get_context_data(**kwargs)
context['weekly_attendance'] = WeeklyAttendance.objects.filter(
distibution=self.object)
context['sales_data'] = SalesData.objects.filter(
distibution=self.object)
context['theatrical_release'] = TheatricalRelease.objects.filter(
distibution=self.object)
return context

Insert request.session in class based Generic view in Django

I'm trying to use request.session to create a 'recent' session key and add the product pages visited by the user to make it accesible in the template, here is my view, what would you guys recommend, I can't seem to go about doing this
class ProductDetail(DetailView):
model = Producto
template_name = 'productos/product_detail.html'
def get_context_data(self, **kwargs):
# Call the base implementation first to get a context
context = super(ProductDetail, self).get_context_data(**kwargs)
# Add in a QuerySet of featured products
context['product_list'] = Producto.objects.filter(featured=True).exclude(pk=self.object.pk)
return context
Thanks for your help!
thanks to Daniel Roseman for the clarification on how to call session from the class based generic view
class ProductDetail(DetailView):
model = Producto
template_name = 'productos/product_detail.html'
def get_context_data(self, **kwargs):
# Call the base implementation first to get a context
context = super(ProductDetail, self).get_context_data(**kwargs)
if not 'recent' in self.request.session or not self.request.session['recent']:
self.request.session['recent'] = [self.object.pk]
else:
recentList = self.request.session['recent']
recentList.append(self.object.pk)
self.request.session['recent'] = recentList
# Add in a QuerySet of featured products
context['product_list'] = Producto.objects.filter(featured=True).exclude(pk=self.object.pk)
context['recent_list'] = Producto.objects.filter(pk__in=recentList)
return context

Categories