I have the following class based view in which I perform to queryset:
class PatientDetail(LoginRequiredMixin, DetailView):
model = PatientProfile
template_name = 'patient_detail.html'
context_object_name = 'patientdetail'
def get_context_data(self, **kwargs):
context=super(PatientDetail, self).get_context_data(**kwargs)
queryset= RehabilitationSession.objects.filter(patient__slug=self.kwargs['slug'])
context.update({'patient_session_data': queryset,})
return context
When I acces the value of patient_session_data key sent, in my template:
{% extends "base.html" %}
{% block content %}
{{patient_session_data}}
{% endblock content %}
I get this the QuerySet object:
<QuerySet [<RehabilitationSession: Hernando Zambrano Cuellar>, <RehabilitationSession: Hernando Zambrano Cuellar>, <RehabilitationSession: Hernando Zambrano Cuellar>]>
I want access to specific attibute named upper_extremity of my RehabilitationSession model, then I make this:
{% for upperlimb in patient_session_data %}
{{upperlimb.upper_extremity}}
{%endfor%}
And I get this in my template:
Izquierda Izquierda Izquierda
This mean, three times the value, because my queryset return me three objects. This is logic.
For access to value of a separate way I make this in my template:
{{patient_session_data.0.upper_extremity}}
And I get: Izquierda
My goal
I unknown the amount of querysets objects RehabilitationSession that will be returned by the queryset executed in my PatientDetail cbv, because the number is dynamic, may be any amount of objects returned.
I want read the value content of each patient_session_data upper_extremity and accord to the value make something in my template,
But I want read it of a dynamic way,without use {{patient_session_data.<0-1-2-3>.upper_extremity}}
For example in a hypotetical case:
#if all objects returned have same value in upper_extremity
{% if patient_session_data.upper_extremity == 'Izquierda' %}
<div class="col-md-10">
<h5>Segmentos corporales a tratar</h5>
<small>Codo - mano - falange</small>
</div>
{%endif%}
I think that I have count the amount of objects and make with them some actions, because I don't have clear ...
How to can I access of a individual way to the objects returned of a dynamic way without matter the objects number returned?
UPDATE
I did have not taken into account the possibility of that all objects returned have different value in upper_extremity. This mean that I wish that when the queryset objects returned have a mix of values of Izquierda Derecha Izquierda also make something.
In this moment (with the support provided by #2ps when the querysets objects all have upper_extremity='Izquierda' make something and when the querysets objects all have upper_extremity='Derecha' make something.
Is want perform some actions when the values of upper_extremity of the querysets objects may be:
Object queryset 1: upper_extremity='Derecha'
Object queryset 2:upper_extremity='Izquierda'
Object queryset 3:upper_extremity='Derecha'
. ..
and so...
According to the solution of #2ps I think that the core of this assesment is in:
# Perform a set union with the values of 'upper_extremity' attribute for remove the repeated values(Izquierda and Derecha) and distinct them
upper_extremities = set(queryset.values_list('upper_extremity', flat=True).distinct())
# Create a empty dict like data destiny
all_match = dict()
# Iterate on all values possible
for value in ('Izquierda', 'Derecha', ): # add any other values you want here
# Fill the dictionary always that the longitude of upper_extremities (values get) be equal to 1 (?)
all_match[value] = value in upper_extremities and len(upper_extremities) == 1
# Send the result to the template
context['all_match'] = all_match
In my template I think ...
{% if all_match.Izquierda and all_match.Derecha %}
<div class="col-md-10">
<h5>Segmentos corporales a tratar</h5>
<small>Codo - mano - falange</small>
</div>
{%endif%}
How to can I cover this another scenary when the objects querysets have a mix of upper_extremity values (Derecha and Izquierda)
Thanks and excuse me for the new question, I have not clear this process.
Several possible approaches you can take here. For the all izquierda or all derecha, here is what you can do:
class PatientDetail(LoginRequiredMixin, DetailView):
model = PatientProfile
template_name = 'patient_detail.html'
context_object_name = 'patientdetail'
def get_context_data(self, **kwargs):
context=super(PatientDetail, self).get_context_data(**kwargs)
queryset= RehabilitationSession.objects.filter(patient__slug=self.kwargs['slug'])
context['patient_session_data'] = queryset
# get a list of all upper extremities
upper_extremities = set(queryset.values_list('upper_extremity', flat=True).distinct())
all_match = dict()
for value in ('Izquierda', 'Derecha', ): # add any other values you want here
all_match[value] = value in upper_extremities and len(upper_extremities) == 1
context['all_match'] = all_match
return context
And in your template
#if all objects returned have same value in upper_extremity
{% if all_match.Izquierda or all_match.Derecha %}
<div class="col-md-10">
<h5>Segmentos corporales a tratar</h5>
<small>Codo - mano - falange</small>
</div>
{%else%}
{# here we handle all cases with mixed results #}
{%endif%}
Related
I'm using a Key-Value model to make static template's data dynamic. This is my model's code:
class SiteDataKeyValue(models.Model):
key = models.CharField(max_length=200, verbose_name="Text title")
value = models.TextField(verbose_name="Text")
def __str__(self):
return self.key
For each static page, e.g. About us or Contact us, first I've created the texts in the admin panel. Then, I've written the views below:
class ContactUsPageView(ListView):
"""In this view we are fetching site data from database, in a function named get_context_data.
In this function, we filter the model for objects that contains the word 'contact', therefore
fetching data referring to the about page only. After that the context list is sent to the
template, and there with a for loop and an if statement, each key, value set is chosen for the proper place.
"""
model = SiteDataKeyValue
template_name: str = "core/contact-us.html"
def get_context_data(self, **kwargs):
context = super(ContactUsPageView, self).get_context_data(**kwargs)
context["contact_page_data"] = self.model.objects.filter(key__contains="contact")
return context
Finally, in the template, I'm trying to use these data with a for loop and an if statement as shown below:
{% for text in contact_page_data %}
{% if text.key == "contact us factory address" %}
<p class="card-text"><i class="bi bi-geo-alt me-2">
{{ text.value }}
</p>
{% endif %}
{% endfor %}
I have just one object with this exact key "contact us factory address" in my database. But when the template is rendered, the icon <i class="bi bi-geo-alt me-2"> which is INSIDE of the if statement, gets printed 11 times (NOTE: Just the icon gets printed 11 times, and the value actually gets printed just once)! (11, is the total number of objects the ContactUsPageView view has sent to this template containing the word "contact".) Why is this happening? Why my icon that is INSIDE the if statemnet is shown 11 times instead of just once?
UPDATE: This problem was solved by adding </i> closing tag and there was no errors in other codes.
I'm new to Django, and I'm working on my first real (i.e., non-tutorial) project. I have class-based ListViews on four models, and the lists can be filtered in various ways. The user can click on anything in a filtered list to get a DetailView of the item. This is all straightforward and works fine.
I would like to have Previous and Next buttons on the detail pages that allow the user to step through the current filtered set in the default order (which is not date or id). I've found various bits and pieces on StackOverflow and elsewhere that look like parts of a solution, but I haven't been able to figure out how to make them all work together in my views.
Here is slightly simplified code for one of my tables. "Works" are various items (plays, operas, ballets, etc.) that were performed in one of two theaters.
models.py
class Work(models.Model):
title = models.CharField(max_length=100)
sort_title = models.CharField(max_length=100)
genre = models.CharField(max_length=50)
class Meta:
ordering = ['sort_title']
The field sort_title strips off articles at the beginnings of titles (which are in German and French) and deals with accented characters and the like, so that the titles will sort correctly alphabetically. This is the order of the filtered sets, and I want to retain that order for the Previous and Next buttons.
views.py
class WorkList(ListView):
context_object_name = 'works'
model = Work
paginate_by = 50
template_name = 'works.html'
def get_queryset(self):
query = self.request.GET.get('q')
if query is not None:
return Work.objects.filter(Q(title__icontains=query))
else:
return Work.objects.all()
class WorkDetail(DetailView):
context_object_name = 'work'
model = Work
template_name = 'work.html'
At the moment, the user can only filter Works by title, but I may add the possibility of filtering by genre (hence the Q, which I'm already using for other views). I'm using Bootstrap 4, and I would use some version of the following for the buttons on the detail pages:
<ul class="pagination pt-3">
<li class="page-link">Previous</li>
<li class="page-link ml-auto">Next</li>
</ul>
But since I don't know how to make this work yet, I don't know what the URLs will be.
I've tried django-next-previous, which works well in the shell, but I can't figure out how to make it work in my views. Since I want to preserve the filtered queryset from the ListView and use it in the DetailView, I've also experimented with this approach to saving the queryset in the session: https://gist.github.com/bsnux/4672788. But I haven't been able to figure out how to use this to pass the queryset between the two views.
Any help would be welcome!
The queries
Previous page:
Work.objects.filter(sort_title__lt=self.object.sort_title).reverse().values('id')[:1]
Next page:
Work.objects.filter(sort_title__gt=self.object.sort_title).values('id')[:1]
Next page if sort_title is not unique - note the gte rather than gt:
Work.objects.filter(sort_title__gte=self.object.sort_title).exclude(id=self.object.id).values('id')[:1]
Explanation
filter(...): The Work object is ordered by the sort_title field by default. So by asking for a sort_title that is greater than the sort_title of the current object, so we will find the next Work object in the set.
self.object is how we access the current object from a DetailView.
values('id'): Only select the values we need to reverse() the URL to a different WorkDetail. I'm making a presumption that this is the id field, but if it's not, it can be substituted with another field.
[:1]: Basically just adds LIMIT 1 to the SQL query. We only need the next in the set.
This all keeps the queries lightweight.
Note that these queries only work (using __lt with reverse() for previous page and __gt for next page) because the default ordering of the Work model is by sort_title ascending.
Putting it together
Given that your ListView and DetailView will be sharing the same queryset logic, it might make sense to use a mixin, for example:
class WorkQueryMixin:
def get_queryset(self):
query = self.request.GET.get('q')
if query is not None:
return Work.objects.filter(Q(title__icontains=query))
else:
return Work.objects.all()
The query parameter could be returned in a different way (e.g. through the session data).
Getting it into context, for example for the next page:
class WorkDetail(WorkQueryMixin, DetailView):
context_object_name = 'work'
model = Work
template_name = 'work.html'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
next_id = (
self.get_queryset()
.filter(sort_title__gt=self.object.sort_title)
.values('id')[:1]
)
# There may be no next page
if next_id:
context['next_id'] = next_id[0]['id']
return context
Many thanks to elyas for the great detailed initial answer and for taking the time to coach me through the details of the implementation. I now have functional Previous and Next buttons that retain the current queryset. Here is the code I ended up with, including both Previous and Next buttons, with a genre filter added:
views.py
class WorkQueryMixin:
def get_queryset(self):
query = self.request.GET.get('q')
if query is not None:
return Work.objects.filter(Q(title__icontains=query) |
Q(genre__icontains=query))
else:
return Work.objects.all()
class WorkList(WorkQueryMixin, ListView):
context_object_name = 'works'
model = Work
paginate_by = 50
template_name = 'my_app/works.html'
class WorkDetail(WorkQueryMixin, DetailView):
context_object_name = 'work'
model = Work
template_name = 'my_app/work.html'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
prev_pk = (
self.get_queryset()
.filter(sort_title__lt=self.object.sort_title)
.reverse().values('pk')[:1]
)
# There may be no next page
if prev_pk:
context['prev_pk'] = prev_pk[0]['pk']
next_pk = (
self.get_queryset()
.filter(sort_title__gt=self.object.sort_title)
.values('pk').values('pk')[:1]
)
# There may be no next page
if next_pk:
context['next_pk'] = next_pk[0]['pk']
return context
In the template for the Works list view, using classes from Bootstrap 4:
<ul>
{% for work in works %}
<a href="{% url 'my_app:work' work.pk %}?{{
request.GET.urlencode }}" class="list-group-item list-group-
item-action">
{% if work.title == "No title" %}
{{work.title}}
{% else %}
<em>{{work.title}}</em>
{% endif %} ({{work.genre}})
</a>
{% empty %}
<p>No works match this search</p>
{% endfor %}
</ul>
And in the template for the Work detail view:
<ul class="pagination pt-3">
{% if prev_pk %}
<li class="page-link"><a href="{% url 'my_app:work' prev_pk %}?{{
request.GET.urlencode }}">Previous</a></li>
{% endif %}
{% if next_pk %}
<li class="page-link ml-auto"><a href="{% url 'my_app:work' next_pk
%}?{{ request.GET.urlencode }}">Next</a></li>
{% endif %}
</ul>
I want to manually render the form in my template, but what I'm trying is not yielding the expected result, and it is not apparent as to why.
class MyForm(forms.Form):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['field1'] = forms.BooleanField()
self.fields['field2'] = forms.BooleanField()
systems = System.objects.all()
for i in range(len(systems)):
self.fields['s' + str(i)] = forms.BooleanField()
self.fields['field3'] = forms.BooleanField()
self.initial_fields = [self.fields['field1'], self.fields['field2']]
now when I do this in my template:
{% for field in form.visible_fields %}
{{ field }}
{% endfor %}
it returns what you would expect...after looking up the method visible_fields it simply returns a list of fields. So in theory, if I create my own list of fields as with self.initial_fields, the form generator should render the following same as above:
{% for field in form.initial_fields %}
{{ field }}
{% endfor %}
but instead I get this output in my template:
<django.forms.fields.BooleanField object at 0x000001242F51E588>
<django.forms.fields.BooleanField object at 0x000001242F51E400>
I'm assuming I'm missing some initialization of the field itself? I don't understand why one works and the other doesn't. Anyone know?
You need to get the bound field object and not the field itself. It's not really a clean way of doing so, but if you are looking to hack around it, you should do like so,
...
self.initial_fields = [self.fields['field1'].get_bound_field(self, 'field1'),
self.fields['field2'].get_bound_field(self, 'field2')]
...
Hope this helps!
1. define a class in the models.py which was created in my own app.
class Article(models.Model):
headline = models.CharField(null=True,blank=True,max_length=200)
content = models.TextField()
def __str__(self):
return self.headline
2. define a function in views.py
from firstapp.models import People,Article
def index(request):
article_list = Article.objects.all()
context = {}
context['article_list'] = article_list
index_page = render(request, 'first_web_2.html', context)
return index_page
The question is: Is the article_list a list?how should I understand "context['article_list'] = article_list"?
Variable article_list is a queryset, which is a collection of objects from your database stemming from the query Article.objects.all(). This particular query is much like SELECT * FROM Article.
The context is a dictionary where string 'article_list' is the key and the variable article_list is the value. The context is passed to your template via the render method where the key is used in your template to render the associated value.
Since you are passing a collection you would have to perform a loop on it in your template. For example, this would render an unordered list of headlines. Note the use of the dot operator to access headline.
<ul>
{% for a in article_list %}
<li> {{ a.headline}} </li>
{% endfor %}
</ul>
article_list is not a list, it's a QuerySet. QuerySets are representations of SQL queries via Django's Object Relational Mapper (ORM). It's easy to see them as lists but they are quite different. In any case, you should read Django's documentation about them.
As for context, you can think of it as passing variables that you can access in your templates. It could be strings, numbers, lists, QuerySets, dictionaries etc. In this case, you want to be able to access your all your Articles in the template, likely so that you can loop through them like {% for article in article_list %}. This allows you to then call the attributes like article.headline and article.content in your template.
QuerySet's in Django are iterable so article_list is not directly a list.
context['article_list'] = article_list
context is an dict and the entry 'article_list' is sign to the query result article_list.
In the template you can get access to the query set like
{% for article in article_list %}
{{ article }}
{% endfor %}
You question is hard to understand but I think I got the right answer. You need to read the framework documentation to understand "what that mean":
context: https://docs.djangoproject.com/en/2.0/ref/templates/api/#django.template.Context
queryset (the list) : https://docs.djangoproject.com/en/2.0/ref/models/querysets/#when-querysets-are-evaluated
I am changing a list of field's values upon viewing, but I want to pass the unchanged values to the context. The case here is of a primitive notification system where upon viewing the notification, it should change its status to viewed.
views.py
class Notification(TemplateView):
template_name='myapp/notification.html'
def get_context_data(self, **kwargs):
user = self.request.user
user_unread = user.notification_set.filter(viewed=False)
user_read = user.notification_set.filter(viewed=True)
context = super(Notification, self).get_context_data(**kwargs)
context.update({'user_unread': user_unread, 'user_read': user_read})
for msg in user_unread:
msg.viewed = True
msg.save()
return context
The Problem with this code however, is that I am getting duplicated values in the read and unread lists, even though I have saved the new values to the model after updating the context that is passed to the template.
template:
Unread:
<ul>
{% for msg in user_unread %}
<li> {{ msg }} </li>
{% endfor %}
</ul>
Already read:
<ul>
{% for msg in user_read %}
<li> {{ msg }} </li>
{% endfor %}
</ul>
On a sidenote, I am new to CBVs and if there if my view code above could be improved I'd love some pointers.
This is due to the lazy nature of querysets. A queryset does not actually touch the database until you evaluate it, which usually occurs when you iterate. So in your code, you iterate through user_unread in the view, to set the read status: so the contents of the queryset are fixed at that point. But you don't iterate through user_read until you reach the template, so the query is not made until then, after you have updated all the unread notifications.
The way to fix this is to explicitly evaluate the read queryset in the view, before you update the unread ones. You can do this by simply calling list on it:
context.update({'user_unread': user_unread, 'user_read': list(user_read)})
for msg in user_unread:
...
You can try getting another duplicate query set to update the objects. Use non-updated for template context and update only another one.
Like:
def get_context_data(self, **kwargs):
user = self.request.user
user_unread = user.notification_set.filter(viewed=False)
#Do some operation on qs so that it gets evaluated, like
nitems = len(user_unread)
#get another queryset to update
user_unread_toupdate = user.notification_set.filter(viewed=False)
user_read = user.notification_set.filter(viewed=True)
context = super(Notification, self).get_context_data(**kwargs)
context.update({'user_unread': user_unread, 'user_read': user_read})
#Use 2nd queryset
for msg in user_unread_toupdate:
msg.viewed = True
msg.save()
return context
Django would cache for each queryset differently. So once evaluated user_unread will have its own copy of objects.
Although, its not very elegant/efficient as multiple copies of similar queryset are loaded, so if number of records high, it will be slower.