Django: transfering two primary keys to new detail view - django

I'm trying to create a detail view where I use employee and subject name to show the evaluations created in the specific subject.
I currently have this detail where, where I'm looping through the subject names and displaying all the evaluations for the subject for the employee. The view is using employee as the primary key.
So when I press on the subject name from the loop below, I wish to go to a new view where I show the evaluations for the subject by the specific employee.
Employee template
{% for subject in subject_list %}
<h5>{{ subject.subjectname }}</h5>
{% for evaluation in subject.evaluation_set.all %}
{{ evaluation.instructor }}
{{ evaluation.ma }}
{% endfor %}
{% endfor %}
I can't figure out what the best way to do this is. I believe using the employee primary key for the new view is correct, but how do I "transfer" the subject id from the loop to the next view?
Alternatively I can use the subject id for the view but then I don't understand how to "transfer" the employee over.
Employee view with subjects
class EmployeeEvalDetailView(DetailView):
template_name = 'evalsys/evalueringer/se_alle_evalueringer.html'
model = Employee
def get_context_data(self, **kwargs):
context = super(EmployeeEvalDetailView, self).get_context_data(**kwargs)
context['fagene'] = Subject.objects.all().prefetch_related(Prefetch('evaluation_set', queryset=Evaluation.objects.filter(ma=self.object)))
return context
Employee model
class Medarbejder(models.Model):
id = models.AutoField(primary_key=True)
slug = models.SlugField(max_length=200)
ma = 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.")
subject = models.ManyToManyField('Subject', related_name='medarbejder', through='Evaluering')
Evaluering model
class Evaluering(models.Model):
id = models.AutoField(primary_key=True)
oprettet = models.DateTimeField(auto_now_add=True)
opdateret = models.DateField(auto_now=True)
ma = models.ForeignKey('Employee', on_delete=models.CASCADE, null=True)
subjectname = models.ForeignKey('Subject', on_delete=models.CASCADE, null=True)
instructor = models.ForeignKey(User, on_delete=models.CASCADE, null=True)
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)
New detail view with multiple PK's
class FagEvalDetailView(DetailView):
model = Employee
template_name = 'evalsys/evalueringer/eksporter/detail_fag_eval.html'
def get_context_data(self, **kwargs):
context = super(FagEvalDetailView, self).get_context_data()
context['pk_alt'] = Subject.objects.get(id=self.kwargs.get('pk_alt', ''))
return context

Related

ManytoMany query and template rendering from one model to another

I'm new to Django. I had two different questions.
I can't query between one of my model and another model (ManyToMany). I can do this with the shell, but I couldn't handle it in the template.
I cannot assign a default value from one model to another model's field.
For the first question;
What I want to do is show values for multiple options. For this, I could make a query similar to this in the shell:
room[0].room_type_id.all()
But I can't do this in the template. On the other hand, when I want to show it with display, it returns empty. What I want to do here; returning the room types for each room or or accessing the room_cost of the RoomType class and displaying it in the template, repeated for each room type.
{% for room in rooms %}
<h3 class="card-title pricing-card-title"> {{room.room_type_id_display}} </h3>
{% endfor %}
My second question is;
To set the value from the property of a different model as default in the other model field. That is, to assign the value returned from the total_price of the Booking model to the price field in the Payment model by default.
I would appreciate it if anyone could provide documentation or resources on the subject.
class RoomType(models.Model):
ROOM_CHOICES = (
('1', 'O),
('2','T'),
('3', 'Th'),
('4','F'),
('5','Fi')
)
room_type = models.CharField(max_length=50,choices=ROOM_CHOICES)
room_type_des = models.TextField(blank=True,null=True)
room_cost = models.IntegerField()
def __str__(self):
return str(self.room_type)
class Room(models.Model):
room_number = models.IntegerField()
room_des = models.TextField(blank=True,null=True)
room_availabe = models.BooleanField(default=True)
room_type_id = models.ManyToManyField(RoomType)
def __str__(self):
return str(self.room_number)
class Booking(models.Model):
room_number_id = models.ForeignKey(Room,on_delete=models.DO_NOTHING)
customer_id = models.ManyToManyField(Customer)
check_in = models.DateTimeField(auto_now_add=True)
check_out = models.DateTimeField(auto_now_add=False,auto_now=False,auto_created=False, null=True)
status = models.BooleanField(default=False)
#property
def calculate_day(self):
day = self.check_out - self.check_in
return str(day.days)
#property
def total_price(self):
day = self.check_out - self.check_in
price = self.room_number_id.room_type_id.room_cost
return price*day.days
class Payment(models.Model):
booking_id = models.ForeignKey(Booking,on_delete=models.DO_NOTHING)
ACCEPT_CHOICES = (
('N','N'),
('K','K'),
)
payment_type = models.CharField(max_length=1,choices=ACCEPT_CHOICES)
price = models.IntegerField()
payment_detail = models.TextField()
Here's a small modification: don't use "_id", because it's not an id, it's a real instance of the foreign model.
Then, use "related_name", and think like "if I start from the opposite side, what name should I use?" (it's always plural).
And for your (2), you can't set a default value for a "in-between table": a ManyToMany field create a "join" table to join the two other tables. You can only set a default value for OneToOne and ForeignKey fields.
class RoomType(models.Model):
ROOM_CHOICES = (
('1', 'O),
('2','T'),
('3', 'Th'),
('4','F'),
('5','Fi')
)
room_type = models.CharField(max_length=50,choices=ROOM_CHOICES)
room_type_des = models.TextField(blank=True,null=True)
room_cost = models.IntegerField()
def __str__(self):
return str(self.room_type)
class Room(models.Model):
room_number = models.IntegerField()
room_des = models.TextField(blank=True,null=True)
room_availabe = models.BooleanField(default=True)
room_type = models.ManyToManyField(RoomType, related_name="rooms")
def __str__(self):
return str(self.room_number)
class Booking(models.Model):
room = models.ForeignKey(Room, related_name="bookings", on_delete=models.DO_NOTHING)
customer = models.ManyToManyField(Customer, related_name="bookings")
check_in = models.DateTimeField(auto_now_add=True)
check_out = models.DateTimeField(auto_now_add=False,auto_now=False,auto_created=False, null=True)
status = models.BooleanField(default=False)
#property
def calculate_day(self):
day = self.check_out - self.check_in
return str(day.days)
#property
def total_price(self):
day = self.check_out - self.check_in
price = self.room_number.room_type.room_cost
return price * day.days
class Payment(models.Model):
booking = models.ForeignKey(Booking, related_name="payments", on_delete=models.DO_NOTHING)
ACCEPT_CHOICES = (
('N','N'),
('K','K'),
)
payment_type = models.CharField(max_length=1,choices=ACCEPT_CHOICES)
price = models.IntegerField()
payment_detail = models.TextField()
If you want all your room types, it's:
RoomType.objects.all()
If you want to "send" all types to a template, use get_context_data like this:
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context["room_types"] = RoomType.objects.all()
return context
and in your template:
{% for room_type in room_types %}
{{ room_type }}
{% endfor %}
For your template (and with my models code above), you could do:
{% for room in rooms %}
<h3 class="card-title pricing-card-title"> {{ room.room_type }} </h3>
{% endfor %}
And if you want to show all options in a form, it's another subject, too long for a simple answer here, read the official documentation here.

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

Django: Problem with filtering products in order

I'm trying to make account view in my django-shop. I want to display information about the order and the ordered goods. I have a ProductInOrder model with foreign key to Order. Now I want to filter the ordered goods by order. But something is going wrong.
class Order(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
ref_code = models.CharField(max_length=15)
items = models.ForeignKey(Cart, null=True ,on_delete=models.CASCADE, verbose_name='Cart')
total = models.DecimalField(max_digits=9, decimal_places=2, default=0.00)
first_name = models.CharField(max_length=100)
last_name = models.CharField(max_length=100, blank=True)
phone = models.CharField(max_length=20)
buying_type = models.CharField(max_length=40, choices=BUYING_TYPE_CHOICES,
default='Доставка')
address = models.CharField(max_length=250, blank=True)
date_created = models.DateTimeField(default=timezone.now)
date_delivery = models.DateTimeField(default=one_day_hence)
comments = models.TextField(blank=True)
status = models.CharField(max_length=100, choices=ORDER_STATUS_CHOICES,
default='Принят в обработку')
class ProductInOrder(models.Model):
order = models.ForeignKey(Order, on_delete=models.CASCADE)
product = models.ForeignKey(Product, on_delete=models.CASCADE)
quantity = models.IntegerField(default=1)
item_cost = models.DecimalField(max_digits=10, decimal_places=2, default=0.00)
all_items_cost = models.DecimalField(max_digits=10, decimal_places=2, default=0.00)
And views.py
def account_view(request):
order = Order.objects.filter(user=request.user).order_by('-id')
products_in_order = ProductInOrder.objects.filter(order__in=order)
categories = Category.objects.all()
instance = get_object_or_404(Profile, user=request.user)
if request.method == 'POST':
image_profile = ProfileImage(request.POST, request.FILES, instance=instance)
if image_profile.is_valid():
avatar = image_profile.save(commit=False)
avatar.user = request.user
avatar.save()
messages.success(request,
f'Ваш аватар был успешно обновлен!')
return redirect('ecomapp:account')
else:
image_profile = ProfileImage()
context = {
'image_profile': image_profile,
'order': order,
'products_in_order': products_in_order,
'categories': categories,
'instance': instance,
}
return render(request, 'ecomapp/account.html', context)
This line products_in_order = ProductInOrder.objects.filter(order__in=order) doesn't work.
Any help please.
Unless you explicityly mention order_by in ProductInOrder queryset, it will order by its default setup, which is mentioned in ProductInOrder model's meta class(if its not mentioned, then default ordering is pk). So using following line should resolve your issue:
ProductInOrder.objects.filter(order__in=order).order_by('-order')
But an improved answer is like this:
products_in_order = ProductInOrder.objects.filter(order__user=request.user).order_by('-order')
In this way, you can remove line order = Order.objects.filter(user=request.user).order_by('-id') from your code. Whats happening here is that, django allows nested filtering, so you can filter by order__user which will allow you order by user from Order model. You don't need to make a filter for Order separately.
Update:
I am not sure, probably you are looking for this:(in template)
{% for o in order %}
{% for po in o.productinorder_set.all %}
{{ po.product }}
{{ po.item_cost }}
{% endfor %}
{% endfor %}
Here I am using reverse relation between Order and ProductInOrder here.

Save a list of objects in model as a field

I'm trying to write internet-shop, and I have a model Order:
class Order(models.Model):
state_choices = ('ACTIVE', 'COMPLETED', 'FROZEN')
order_date = models.DateTimeField(auto_now_add=True)
delivery_time = models.CharField(max_length=100)
address_city = models.CharField(max_length=40)
address_street = models.CharField(max_length=40)
address_building = models.CharField(max_length=40)
state = models.CharField(max_length=200, default='ACTIVE')
products = models.ForeignKey(OrderProduct)
client = models.ForeignKey(CustomUser)
And OrderProduct:
class OrderProduct(models.Model):
product = models.ForeignKey(Product)
product_ammount = models.IntegerField()
As you can see, user can add to order different products and different ammount of each product. So, with current models, I can add to order only one type of product. Then I rewrite it in the next way:
class Order(models.Model):
state_choices = ('ACTIVE', 'COMPLETED', 'FROZEN')
order_date = models.DateTimeField(auto_now_add=True)
delivery_time = models.CharField(max_length=100)
address_city = models.CharField(max_length=40)
address_street = models.CharField(max_length=40)
address_building = models.CharField(max_length=40)
state = models.CharField(max_length=200, default='ACTIVE')
client = models.ForeignKey(CustomUser)
class OrderProduct(models.Model):
product = models.ForeignKey(Product)
order = models.ForeignKey(Order)
product_ammount = models.IntegerField()
And in a view, when I need to get a user's orders, I just do next: Order.objects.get(client=request.user).orderproduct_set
But I think that it's not correct. How to rebuild these models to gain the desired result?
In my opinion the second approach is perfectly fine.
One small error in the question is that the query uses get() instead of filter(). This will lead to an exception once one user has more than one order.
So, instead of the get() it would be:
orders = Order.objects.filter(client=request.user)
for order in orders:
print order.orderproduct_set.all()
To use this in a template (question from the comments) it is enough to pass the orders:
views.py
class MyView(View):
def get(self, request):
ctx = {
'orders': Order.objects.filter(client=request.user)
}
return render(request, 'my/template.html', ctx)
my/template.html
{% for order in orders %}
{% for item in order.orderproduct_set.all %}
{{ item.product_amount }}x {{ item.product }}<br/>
{% endfor %}
{% endfor %}

Queries over ManyToManyField with variable set in a sessions user

Here is my models:
class Clients(models.Model):
client_name = models.CharField(max_lenght=100)
commentaire_clients = models.TextField(blank=False)
date_demande = models.TimeField(auto_now_add=True)
choix = models.ManyToManyField('Agence', blank=True)
def __unicode__(self):
return unicode(self.date_demande)
class Market(models.Model):
nom_market = models.CharField(max_length=200)
code_postal_market = models.ManyToManyField('Lieux', blank=True)
statut_vip = models.BooleanField(default=False)
def __unicode__(self):
return self.nom_market
class Lieux(models.Model):
code_postal = models.CharField(max_length=200)
ville = models.CharField(max_length=200)
region = models.CharField(max_length=200)
departement = models.CharField(max_length=200)
longitude = models.DecimalField(max_digits=9, decimal_places=6)
latitude = models.DecimalField(max_digits=9, decimal_places=6)
pays = models.CharField(max_length=100)
def __unicode__(self):
return unicode(self.code_postal)
Here is my view:
def comparelist(request):
if request.session.get('code_postal'):
poste = request.session.get('code_postal')
else:
poste = "RATE"
market_match = Market.objects.filter(statut_vip=False, code_postal_market = poste)
market_match_vip = Market.objects.filter(statut_vip=True)
#edit bis repetita Market replace Agence models
return render_to_response('compare.html', {
'code_postale': poste,
'bien_immobilier': bien,
'listing_agence' : market_match ,
'listing_vip' : market_match_vip ,
})
What I am trying to do is to make a query that will give me all the market that match:
- statut_vip = False
- code_postal_market = poste (that I obtain from user session from a form on the previous page
then I try to render it in my templates via:
{% for mes_market in listing_vip %}
<br>{{mes_market.nom_market}}
<br>{{mes_market.statut_vip}}
{% endfor %}
edit
here is my template for listing_agence (same as the previous one but with the right reference) sorry for the error.
{% for mes_agences in listing_agence %}
<br>{{mes_agences.nom_market}}
<br>{{mes_agences.statut_vip}}
{% endfor %}
My second queries to list all VIP clients do work but when I try to filter via the postal code given by the user via a form (and keeping the record via sessions)
nothing appears.
Thank you for your help!
I finally made it!
I replaced:
market_match = Market.objects.filter(statut_vip=False, code_postal_market = poste)
by
market_match = Market.objects.filter(statut_vip=False, code_postal_market__code_postal=poste)
code_postal is from the table Lieux