django relationships into a cbv - django

I have this two models:
class MenuGroup(models.Model):
name = models.CharField(max_length=30)
class MenuProduct(models.Model):
name = models.CharField(max_length=30)
ingredients = models.CharField(max_length=250)
price = models.FloatField(null=True, blank=True, default=0.0)
group = models.ForeignKey(MenuGroup, on_delete=models.CASCADE)
I want to get all the records of MenuGroup with all the MenuProduct records related.
Template side i need to have structure like this:
{% for group in menugroups %}
...
{% product in group.menuproducts %}
How to express this into a Django CBV view?

You don't need to do anything, Django gives you that functionality already. The only thing to change is that the default accessor is <modelname>_set, and it's a Manager; so:
{% for product in group.menuproduct_set.all %}

Related

Get Data from all objects of a queryset in django

I have three models Order, OrderEntries and Product
class Order(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
order_entries = models.ManyToManyField(OrderEntries, blank=True)
...
class OrderEntries(models.Model):
product = models.ForeignKey(Product, on_delete=models.CASCADE)
quantity = models.IntegerField()
class Product(models.Model):
title = models.CharField(max_length=1000)
slug = models.SlugField(blank=True, null=True, unique=True)
Now, I have to get history of purchased products. As I can get all previous orders placed by a user like this
queryset = Order.objects.filter(user=self.request.user, client=self.request.client)
But how I can get list of products from all those orders?
If you specify a related_name on the ForeignKey field as follows:
product = models.ForeignKey(Product, on_delete=models.CASCADE, related_name='products')
You could then access the products in your template, for example:
{% for order in orders %}
{% for order_entry in order.order_entries %}
{% for product in order_entry.products.all %}
{{ product }}
Please note: it isn't necessary to set related_name. If you don't set it, you can use BLA_set where BLA is the name of the field e.g.:
{% for product in order_entry.product_set.all %}
maybe this help :
product=[]
for e in queryset:
for ord in e['order_entries']:
for pr in ord['product'] :
product.append(pr)

Django Retrieve Data From Two Tables By Join

Hi I have two tables which are Employee(eid,eName,contact_num,e-mail,adress,salary) and Nurse(nurse_id,e_id) and the "e_id" column of Nurse model has foreign key on 'eid' of Employee model.I know how to filter by specific id, however all of them as list so that, I want to return all nurses from Employee table.You can find my models below. I am new to Django that is why any help or hint is appreciated. Thanks in advance.
class Employee(models.Model):
eid = models.IntegerField(primary_key=True)
ename = models.CharField(db_column='eName', max_length=25) # Field name made lowercase.
contact_num = models.DecimalField(max_digits=12, decimal_places=0)
e_mail = models.CharField(max_length=30)
adress = models.CharField(max_length=250, blank=True, null=True)
salary = models.IntegerField()
class Nurse(models.Model):
nurse_id = models.IntegerField(primary_key=True)
e = models.ForeignKey(Employee, models.DO_NOTHING)
from an instance of employee class you can get the nurses as follows
employee_1 = Employee.objects.first()
nurses = employee_1.nurse_set.all()
can call in templates of list view of employee by
{% for obj in object_list %}
{{ obj.ename }}
{% for nurse in obj.nurse_set.all %}
{{ nurse.eid }}
{% endfor %}{% endfor %}

Django ListView dynamic context data

Given my models:
class Deck(models.Model):
owner = models.ForeignKey(User, on_delete=models.CASCADE)
name = models.CharField(max_length=255)
class Flashcard(models.Model):
owner = models.ForeignKey(User, on_delete=models.CASCADE)
deck = models.ForeignKey(Deck, on_delete=models.CASCADE)
question = models.TextField()
answer = models.TextField()
In my html template, I want to have a table of all the user's decks, each with their number of cards, e.g. like this:
Deck1: 15
Deck2: 22
Deck3: 100
In my views I have:
def get_context_data(self,**kwargs):
context=super(generic.ListView,self).get_context_data(**kwargs)
context['number_decks']=Deck.objects.filter(owner=self.request.user).count
context['number_cards']=Flashcard.objects.filter(owner=self.request.user,deck__name="Deck1").count
number_decks works as expected.
number_cards also works when I manually type the name in.
But how can I do this without specifying the name, so that in the html template I can just say:
{% for deck in deck_list %}
<td>{{deck.name}}</td>
<td>{{number_cards}}</td>
{% endfor %}
And since I won't know the names of the decks that users will create.
I've tried deck__id=deck.id, but I get zeroes.
How can I change either my models or views to get what I want?
You could just write that as a function to your Deck-Model:
class Deck(models.Model):
owner = models.ForeignKey(User, on_delete=models.CASCADE)
name = models.CharField(max_length=255)
def count_flashcards(self):
fc = Flashcard.objects.filter(deck=self).count()
return fc
And in your template:
{% for deck in deck_list %}
<td>{{deck.name}}</td>
<td>{{deck.count_flashcards}}</td>
{% endfor %}
You might want to use a cached attribute decorator here as well

Django: want to loop through _set for pk values only

I'm stuck trying to figure out how to filter my template values with the detail view PK. I have a detail view for my employee. I wish to display my employee's subjects, where I then wish to filter the subjects with the evaluations that have been made for the subject.
I've gotten so far that I can show my subject names, and show all the evaluations for each subject. However, I don't want to show ALL of them I only want to show the ones that exist for the current employee (detailView PK). As you can see in my template, I'm using _set to make the relation, but I have no clue on how to filter the PK into that equation.
Example, what I want:
Subject 1:
Evaluationname - employee Johnny
Evaluationname - employee Johnny
Example, what I currently have:
Subject 1:
Evaluationname - employee Johnny
Evaluationname - employee Chris
I don't want Chris's evaluation, I only wish to filter the primary key, so in this case Johnny's evaluations.
Template
{% for subject in subject_list %}
{{ subject.subejctname }}
{% for evaluation in subject.evaluation_set.all %}
<div>
<p>{{ evaluering.ma }} | {{ evaluering.ma.firstname }} | {{ evaluering.ma.lastname }}</p>
</div>
{% empty %}
<p>No evaluations founds.</p>
{% endfor %}
{% endfor %}
View
class EmployeeDetailView(DetailView):
template_name = 'evalsys/employee/alle_employees_eval.html'
model = Employee
# Uses employee PK to make a detail view
def view_employee_with_pk(self, pk=None):
if pk:
employee = Employee.objects.get(pk=pk)
else:
employee = self.employee
args = {'employee': employee, }
return render(self, 'evalsys/employee/alle_employees_eval.html', args)
def get_context_data(self, **kwargs):
context = super(EmployeeDetailViewDetailView, self).get_context_data(**kwargs)
context['subject_list'] = Subject.objects.all()
return context
Subject Model
class Subject(models.Model):
id = models.AutoField(primary_key=True)
subjectname = models.CharField(max_length=255, help_text="Indtast navnet på faget.")
slug = models.SlugField(max_length=200, unique=True)
Evaluation model
class Evaluation(models.Model):
id = models.AutoField(primary_key=True)
employee_num = models.ForeignKey('Employee', on_delete=models.CASCADE, null=True)
subjectname = models.ForeignKey('Subject', on_delete=models.CASCADE, null=True)
Employee model
class Employee(models.Model):
id = models.AutoField(primary_key=True)
slug = models.SlugField(max_length=200)
employee_num = models.IntegerField(help_text="Indtast medarbejderens MA-nummer. (F.eks 123456)")
firstname = models.CharField(max_length=30, help_text="Indtast medarbejderens fornavn.")
lastname = models.CharField(max_length=30, help_text="Indtast medarbejderens efternavn.")
subjectname = models.ForeignKey('Subject', on_delete=models.CASCADE, null=True)
Reverse relationships (subject.evaluation_set) can be prefetched, this is a technique for reducing the number of database queries made when you access the reverse relationship for many objects in a queryset. When using the following queryset, when you access subject.evaluation_set.all it will not perform an additional DB access as the result has already been cached on each instance
Subject.objects.all().prefetch_related('evaluation_set')
This cached result can be modified by using Prefetch objects. Using these you can limit the contents of subject.evaluation_set.all to only contain the result that you want
Subject.objects.all().prefetch_related(
Prefetch(
'evaluation_set',
queryset=Evaluation.objects.filter(employee=self.employee)
)
)
Your model structure is confusing. Are you able to detail the relationship between employee, subject and evaluation?? You have mentioned you wish to display an employee's subjects, but via your model structure, an employee can have only one subject, as employee is related to the subject by a foreign key.
Below I have suggested some changes to your model names and your model structure so it might make more sense to retrieve your evaluations in the template. Feel free to ask questions about your model design as that is crucial to design your views, templates, etc.
Also please refer here for model naming conventions
Django Foreign Key Reference
Django Model Coding Style (PEP8)
Subject Model
class Subject(models.Model):
id = models.AutoField(primary_key=True)
subject_name = models.CharField(max_length=255, help_text="Indtast navnet på
faget.")
slug = models.SlugField(max_length=200, unique=True)
Employee Model
class Employee(models.Model):
id = models.AutoField(primary_key=True)
slug = models.SlugField(max_length=200)
employee_num = models.IntegerField(help_text="Indtast medarbejderens MA-nummer. (F.eks 123456)")
first_name = models.CharField(max_length=30, help_text="Indtast medarbejderens fornavn.")
last_name = models.CharField(max_length=30, help_text="Indtast medarbejderens efternavn.")
subjects = models.ManyToManyField(Subject, related_name='employee', through='Evaluation')
Evaluation Model
class Evaluation(models.Model):
name = models.CharField(blank=True,max_length=50)
employee = models.ForeignKey('Employee', on_delete=models.CASCADE)
subject = models.ForeignKey('Subject', on_delete=models.CASCADE)
So the assumption, is an employee can have different subjects and the mapping is defined via a through model using many-to-many.
Your DetaiView can then just be
class EmployeeDetailView(DetailView):
template_name = 'evalsys/employee/alle_employees_eval.html'
model = Employee
def get_context_data(self, **kwargs):
context = super(EmployeeDetailViewDetailView,
self).get_context_data(**kwargs)
context['evaluations'] = Evaluation.objects.filter(employee=self.object)
return context
Template
{% for evaluation in evaluations %}
{{ evaluation.subject.subject_name }}
<p>{{ evaluation.name }} | {{ evaluation.employee.first_name }} |
{{evaluation.employee.last_name }}</p>
{% empty %}
<p>No evaluations founds.</p>
{% endfor %}

Filter with Q inside a span relationship

I try to get tasks in context to the project they are belonging to. With an easy Project.objects.all() i get pretty much what i want. How can i filter this queryset in the best way?
Models:
class Project(models.Model):
projectname_text = models.CharField('Projectname', unique=True, max_length=200)
class Task(models.Model):
project = models.ForeignKey(Project, on_delete=models.CASCADE,
related_name='tasks')
author = models.ForeignKey(User, null=True, blank=True, related_name='author')
editor = models.ForeignKey(User, null=True, blank=True, related_name='editor')
I tried with Q, but the result looks like it contains too many items and even items where the request.user is wrong. MAybe using Q is the wrong approach here?
Query in Views:
project_list = Project.objects.filter(Q(tasks__author=request.user) |
Q(tasks__editor=request.user))
Template:
{% for project in project_list %}
html
{% for task in project.tasks.all %}
html
{% endfor %}
{% endfor %}
First off, your query criteria is wrong. author and editor are on model Task not on Project, so you should do:
projects = Project.objects.filter(Q(tasks__author=request.user) |
Q(tasks__editor=request.user))
Secondly, never use list as your variable name, as that would override the default python data structure list, so you can no longer use list() to create a list after your variable declaration.