Django querying models with foreignkey - django

I am creating a project of customer management, in this I want to query some models which are related with foreign key.
I have created these models.
from django.db import models
class Customer(models.Model):
name = models.CharField(max_length=250, null=True)
phone = models.CharField(max_length=10, null=True)
email = models.CharField(max_length=250, null=True)
date_created = models.DateTimeField(auto_now_add=True, null=True)
# to get name as string on behalf of "Customer Object 1" in DB.
def __str__(self):
return self.name
class Tag(models.Model):
name = models.CharField(max_length=250, null=True)
def __str__(self):
return self.name
class Product(models.Model):
# To make a dropdown menu to choose category.
CATEGORY = (
('Indoor', 'Indoor'),
('Out Door', 'Out Door'),
)
name = models.CharField(max_length=250, null=True)
price = models.FloatField(null=True)
category = models.CharField(max_length=250, null=True, choices=CATEGORY)
description = models.CharField(max_length=250, null=True, blank=True)
date_created = models.DateTimeField(auto_now_add=True, null=True)
tags = models.ManyToManyField(Tag)
def __str__(self):
return self.name
class Order(models.Model):
# To make a dropdown menu to choose status.
STATUS = (
('Pending', 'Pending'),
('Out for Delivery', 'Out for Delivery'),
('Delivered', 'Delivered'),
)
customer = models.ForeignKey(Customer, null=True,
on_delete=models.SET_NULL)
product = models.ForeignKey(Product, null=True,
on_delete=models.SET_NULL)
date_created = models.DateTimeField(auto_now_add=True, null=True)
status = models.CharField(max_length=250, null=True, choices=STATUS)
Views.py
from django.shortcuts import render
from .models import *
def home(request):
customers = Customer.objects.all()
orders = Order.objects.all()
total_customers = customers.count()
total_orders = orders.count()
delivered = orders.filter(status='Delivered').count()
pending = orders.filter(status='Pending').count()
front_end_stuff = {'customers': customers, 'orders': orders,
'total_customers': total_customers, 'total_orders': total_orders,
'delivered': delivered, 'pending': pending,
}
return render(request, 'accounts/dashboard.html', context=front_end_stuff)
def products(request):
products_ = Product.objects.all()
return render(request, 'accounts/products.html', context={'products': products_})
def customer(request, pk):
customers = Customer.objects.filter(id=pk)
orders = Order.objects.filter(id=pk)
customer_products = Product.objects.filter(id=pk)
total_orders = orders.count()
front_end_stuff = {'customers': customers, 'orders': orders,
'total_orders': total_orders, 'customer_products': customer_products
}
return render(request, 'accounts/customer.html', context=front_end_stuff)
I want to get status of the orders placed by a specific customer, also i have set the dynamic urls for getting customer profile view page and in it i want to loop through it and print out the status in respective field.
I have attached the image of customer profile view page where i want the data.
customer_profile_view_page
I tried one query which i found on internet is :
customers = Customer.objects.filter(id=pk)
status = customers.order_set.all()
but I get an error
AttributeError: 'QuerySet' object has no attribute 'order_set'
I am using :
Windows 10,
Python 3.7,
Django 3.0.7.

To get the list of all the orders of a customer, you can try -
# this will give the list of orders for customer with id = pk
orders = Order.objects.filter(customer__id=pk) # it's a double underscore
You can iterate each order to fetch the status -
for order in orders:
print(order.status)
Or with what you tried, use .get instead of .filter.
customer = Customer.objects.get(id=pk) # the customer has to be present with this id else it will give an exception.
orders = customer.order_set.all()
Doc

Related

Filter queryset of django inline formset based on attribute of through model

I have a basic restaurant inventory tracking app that allows the user to create ingredients, menus, and items on the menus. For each item on a given menu, the user can list the required ingredients for that item along with a quantity required per ingredient for that item.
Menu items have a many-to-many relationship with ingredients, and are connected via an "IngredientQuantity" through table.
Here are my models:
class Ingredient(models.Model):
GRAM = 'Grams'
OUNCE = 'Ounces'
PIECE = 'Pieces'
UNIT_CHOICES = [
('Grams', 'Grams'),
('Ounces', 'Ounces'),
('Pieces', 'Pieces')
]
user = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True)
name = models.CharField(max_length=200)
unitType = models.CharField(max_length=200, choices=UNIT_CHOICES, verbose_name='Unit')
unitCost = models.DecimalField(max_digits=10, decimal_places=2, verbose_name='Unit Cost')
inventoryQuantity = models.DecimalField(max_digits=10, decimal_places=2, verbose_name='Quantity')
def __str__(self):
return self.name + ' (' + self.unitType + ')'
def totalCost(self):
result = self.inventoryQuantity * self.unitCost
return "{:.0f}".format(result)
class Menu(models.Model):
user = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True)
name = models.CharField(max_length=200)
timeCreated = models.DateTimeField(auto_now_add=True)
timeUpdated = models.DateTimeField(auto_now=True)
def __str__(self):
return str(self.name)
class MenuItem(models.Model):
user = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True)
name = models.CharField(max_length=200)
price = models.DecimalField(max_digits=10, decimal_places=2)
ingredients = models.ManyToManyField(Ingredient, through='IngredientQuantity')
menu = models.ForeignKey(Menu, on_delete=models.CASCADE, null=True)
def __str__(self):
return self.name
def itemCost(self):
relevantIngredients = IngredientQuantity.objects.filter(menuItem=self)
cost = 0
for ingredient in relevantIngredients:
cost += (ingredient.ingredient.unitCost * ingredient.ingredientQuantity)
return cost
class IngredientQuantity(models.Model):
user = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True)
ingredient = models.ForeignKey(Ingredient, on_delete=models.CASCADE)
menuItem = models.ForeignKey(MenuItem, on_delete=models.CASCADE)
ingredientQuantity = models.IntegerField(default=0)
def __str__(self):
return str(self.ingredient)
This is a multi-user app, so when a user creates a new item on a menu and adds ingredients to it, they should only have the option of choosing ingredients they have created, not those of other users. Here is my attempt to do that in my views:
def ItemUpdate(request, pk):
item = MenuItem.objects.get(id=pk)
user = request.user
IngredientQuantityFormset = inlineformset_factory(
MenuItem, IngredientQuantity, fields=('ingredient', 'ingredientQuantity'), can_delete=True, extra=0
)
form = ItemCreateForm(instance=item)
formset = IngredientQuantityFormset(instance=item, queryset=IngredientQuantity.objects.filter(ingredient__user=user))
if request.method == 'POST':
form = ItemCreateForm(request.POST, instance=item)
formset = IngredientQuantityFormset(request.POST, instance=item, queryset=IngredientQuantity.objects.filter(ingredient__user=user))
# rest of view...
I've searched everywhere for how to implement the queryset parameter properly, but I cannot get it to work. When creating an item on a menu, the user still has the ability to choose from every ingredient in the database (including the ones created by other users). I would like the user to only be able to choose from the ingredients they themselves created.
Does anyone know how to do this properly? Thank you!
I received some guidance on Django forums and arrived at a solution which is documented below:
https://forum.djangoproject.com/t/filter-dropdown-options-in-django-inline-formset-based-on-attribute-of-through-model/13374/3

Simple django filter is includes all users’ data rather than that of the logged-in user

I’m new to Django and have built a basic filter that does not filter according to the logged-in user’s data but rather all users’ data, which is incorrect. The filter is for an Automation class, which has a many:many relationship with the Message class (and funnily enough the exact same happens with the message filter).
Views.py:
#login_required(login_url='login')
#allowed_users(allowed_roles=['admin', 'customer'], own_account_only=True)
def automation_list(request, pk):
account = Account.objects.get(id=pk)
automations = account.automation_set.all()
filter = AutomationFilter(request.GET, queryset=automations)
automations = filter.qs
context = {'account': account,
'automations': automations, 'filter': filter}
return render(request, 'automations/automation_list.html', context)
Filters.py:
class AutomationFilter(django_filters.FilterSet):
start_date = DateFilter(field_name='date_joined', lookup_expr='gte')
end_date = DateFilter(field_name='date_joined', lookup_expr='lte')
class Meta:
model = Automation
fields = '__all__'
exclude = ['account', 'date_created']
Models:
class Automation(models.Model):
name = models.CharField(max_length=100)
description = models.CharField(max_length=200)
account = models.ForeignKey(Account, on_delete=models.CASCADE)
date_created = models.DateTimeField(auto_now_add=True, null=True)
messages = models.ManyToManyField(Message, blank=True)
def __str__(self):
return self.name
class Message(models.Model):
name = models.CharField(max_length=100)
subject = models.CharField(max_length=128)
text = models.TextField()
account = models.ForeignKey(Account, on_delete=models.CASCADE)
date_created = models.DateTimeField(auto_now_add=True, null=True)
automations = models.ManyToManyField('automations.Automation', blank=True)
def __str__(self):
return self.name
Why is the filter not just filtering according to the logged-in user? I’d have thought that I’m only passing in the user’s data via this line:
filter = AutomationFilter(request.GET, queryset=automations)
Thanks

Foreign key with multiple models?

I am creating ecommerce website. There are multiple categories. For example, phones, computers and others. I created a model for each of them. In OrderItems I want to foreign key each of them. So, I want to use multiple models in ForeignKey.
models.py
class Telefon(models.Model):
name = models.CharField(max_length=200)
category = models.CharField(max_length=300, choices=TELEFON, default="xiaomi")
price = models.FloatField()
image = models.ImageField(null=True, blank=True)
def __str__(self):
return self.name
#property
def imageURL(self):
try:
url = self.image.url
except:
url = ''
return url
class TV(models.Model):
name = models.CharField(max_length=200)
category = models.CharField(max_length=300, choices=TELEFON, default="xiaomi")
price = models.FloatField()
image = models.ImageField(null=True, blank=True)
def __str__(self):
return self.name
#property
def imageURL(self):
try:
url = self.image.url
except:
url = ''
return url
Product={'Telefon', 'TV'}
class OrderItem(models.Model):
product = models.ForeignKey(Product, on_delete=models.SET_NULL, null=True)
order = models.ForeignKey(Order, on_delete=models.SET_NULL, null=True)
quantity = models.IntegerField(default=0, null=True, blank=True)
date_added = models.DateTimeField(auto_now_add=True)
#property
def get_total(self):
total = self.product.price * self.quantity
return total
So, how can I use multiple models in Foreign Key field in my OrderItems model.

Django model query with Foreign key and get logged in user

Hi I am trying to add "fields" to my django projects that would be calculated based on query..
Basically I have 2 models one is a user which is an extension of Abstract user
class CustomUser(AbstractUser):
pass
and my main model is Project
class Project(models.Model):
id = models.AutoField(primary_key=True)
user = models.ForeignKey(CustomUser, on_delete=models.PROTECT, editable=False)
name = models.CharField(max_length=20, editable=False)
total = models.DecimalField(max_digits=6, decimal_places=2, editable=False)
created = models.DateTimeField(auto_now_add=True, editable=False, null=False, blank=False)
this_month = datetime.datetime.now().month
allprojectsthismonth = Project.objects.filter(created__month=this_month)
def __str__(self):
return self.name
I create Project objects via a web form using this view:
def homepage(request):
if request.method == "POST":
project = Project()
name = request.POST.get('name')
total = request.POST.get('total')
created = datetime.datetime.now()
user = request.user
project.user = user
project.name = name
project.total = total
project.created = created
project.save()
#return HttpResponse(reverse("homepage.views.homepage"))
return render(request, 'homepage.html')
else:
return render(request, 'homepage.html')
What I need now is to have a queryset that gets me the combination of the total of a given user Project object so that I can make calculations on it, how would I go about doing that?
ideally I would get the logged in user and I could add to my view the sum of all Project.object.total with user = currently logged in.
Thanks
EDIT: Would this work?
class Project(models.Model):
id = models.AutoField(primary_key=True)
user = models.ForeignKey(CustomUser, on_delete=models.PROTECT, editable=False)
name = models.CharField(max_length=20, editable=False)
total = models.DecimalField(max_digits=6, decimal_places=2, editable=False)
created = models.DateTimeField(auto_now_add=True, editable=False, null=False, blank=False)
this_month = datetime.datetime.now().month
projectsthismonth = self.objects.filter(created__month=this_month)
def monthlyTotal(self,request):
projectsthismonth.objects.filter(
user=request.user
).aggregate(
sum_total=Sum('total')
)['sum_total']
I seem to be missing something however.
You can obtain the queryset of the Projects where the user is the logged in user with:
Project.objects.filter(user=request.user)
If you want to make simple aggregates (like sum, count, etc.), you better use .aggregate(…) [Django-doc] since then the calculations are done at the database level. For example:
from django.db.models import Sum
Project.objects.filter(
user=request.user
).aggregate(
sum_total=Sum('total')
)['sum_total']
will retrieve the sum of the totals of the Projects with user=request.user, this will be None if there are no projects related to the user at all.
from django.utils.timezone import now
class Project(models.Model):
id = models.AutoField(primary_key=True)
user = models.ForeignKey(CustomUser, on_delete=models.PROTECT, editable=False)
name = models.CharField(max_length=20, editable=False)
total = models.DecimalField(max_digits=6, decimal_places=2, editable=False)
created = models.DateTimeField(auto_now_add=True)
def monthlyTotal(self,user):
this_month = now().month
return Project.objects.filter(
created__month=this_month,
user=user
).aggregate(
sum_total=Sum('total')
)['sum_total']
Note: You can limit views to a view to authenticated users with the
#login_required decorator [Django-doc].

How to Fetch Data in ManyToMany Django And render in view.py

I want to fetch Data to my view.py where the buyer select a specific market. Which Means the Buyer can only see the Market he selects.
class Market(models.Model):
name = models.CharField(max_length=30)
address = models.TextField("Market Address")
interval = models.PositiveIntegerField("Market Day Interval", default=5)
startdate = models.DateField("Initial Date", auto_now=False, auto_now_add=False)
location = models.ForeignKey(Local, related_name='market_location', on_delete=models.CASCADE)
desciption = models.TextField("Good Description")
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
def __str__(self):
return self.name
class Buyer(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, primary_key=True)
market = models.ManyToManyField(Market, related_name='interested_markets')
status = models.BooleanField("User Status", default=False)
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
def __str__(self):
return self.user.username
view.py
#login_required
#buyer_required
def welcome_buyer(request):
buyer = request.user.buyer
market = buyer.interested_markets.all()
return render(request, 'buyer/home.html', {
'market': market
})
you can simply iterate over this using your buyer object:
for market in buyer.market_set.all()
or
markets = buyer.market_set.all()
in your case it would be:
markets = buyer.interested_markets_set.all()
or
markets = buyer.market # you should consider changing it's name to be plural to indicate that it's a list of objects like: buyer.markets
for more details read the docs here: django many to many