ManytoMany query and template rendering from one model to another - django

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.

Related

How to properly use filter method with foreign key in django details view with multiple models

I have two model one model is being used for storing the blog posts and another model is being used for taking the ratings and comments. Below are two my models
# Models Code
class Products(models.Model):
name = models.CharField(max_length=50)
img = models.ImageField(upload_to='productImage')
CATEGORY = (
('Snacks','Snacks'),
('Juice','Juice'),
)
category = models.CharField(max_length=50, choices=CATEGORY)
description = models.TextField()
price = models.FloatField()
review = models.TextField()
# Rating Model
class Rating(models.Model):
product = models.ForeignKey(Products, on_delete=models.CASCADE)
user = models.ForeignKey(User, on_delete=models.CASCADE)
stars = models.IntegerField(validators=[MinValueValidator(1),MaxValueValidator(5)])
comment = models.TextField()
#Views Code
class ProductListView(ListView):
model = Products
template_name = 'products.html'
context_object_name ='Products'
class ProductDetailView(DetailView):
model = Products
def get_context_data(self, **kwargs):
# Call the base implementation first to get a context
context = super().get_context_data(**kwargs)
context['Rating'] = Rating.objects.filter(self.product_id) # How can i get the comments only for that specific product?
return context
In details-view how should I filter to fetch the comments for that specific product only ?
no need to write separate context for that in ProductDetailView, you can do it as follows in templates
{% for rate in object.rating_set.all %}
{{ rate.comment }}
{% endfor %}

Django: transfering two primary keys to new detail view

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

Use two models in ListView

I have a ListView for every budget category in my list of transactions. For each of these views, I'd like to show the actual budget for this category. For instance, my Bill budget has sub budgets for rent, insurance, phone, etc which is stored in a separate model from the transactions. The current ListView just sends the transactions filtered by budget type:
class BillListView(ListView):
model = Transaction
template_name = 'budget/base_transactions.html'
context_object_name = 'transactions'
paginate_by = 10
queryset = Transaction.objects.filter(budget_type__exact='bill')
Is there a way to send the sub budget data from my budget database as well so I can display it at the top of the template?
My models:
class Transaction(models.Model):
date = models.DateField(default=datetime.date.today)
description = models.CharField(max_length=100, default="")
category = models.CharField(max_length=100, default="")
amount = models.DecimalField(max_digits=10, decimal_places=2, default=0.0)
budget_type = models.CharField(max_length=100, default="")
def __str__(self):
return self.description + ' ' + str(self.amount)
class Budget(models.Model):
category = models.CharField(max_length=100, default="")
sub_category = models.CharField(max_length=100, default="")
amount = models.DecimalField(max_digits=10, decimal_places=2, default=0.0)
If you want to pass data of another model through the ListView, you might be looking for overriding the get_context_data method of ListView. Then you can be able to pass required data along with our model objects.
For example:
In your context, where you want to show data about the budget objects, you can change your BillListView view as:
class BillListView(ListView):
model = Transaction
template_name = 'budget/base_transactions.html'
context_object_name = 'transactions'
paginate_by = 10
queryset = Transaction.objects.filter(budget_type__exact='bill')
def get_context_data(self, **kwargs):
context = super(BillListView, self).get_context_data(**kwargs)
context['budgets'] = Budget.objects.filter(category__exact='bill') //filter as per required
return context
Then you can access the budget objects in the template by looping through budgets as:
{% for budget in budgets %}
{{ budget.amount }}
{{ budget.sub_category }}
{% endfor %}

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