Django spanning relationships - django

I've read the documentation but am still coming up with errors. I have Users placing Orders for Catalog objects. I'd like to create a query which returns all Users that have an Order containing a specific Catalog item.
Here are my models:
class Catalog(models.Model):
name = models.CharField(max_length=100)
price = models.IntegerField()
def __unicode__(self):
return self.name
class Annual(models.Model):
catalog = models.OneToOneField(Catalog, blank=True, null=True, related_name='annual_products')
year_id = models.IntegerField(max_length=4)
start_date = models.CharField(max_length=10)
end_date = models.CharField(max_length=10)
date = models.DateTimeField(auto_now_add=True, blank=True)
def __unicode__(self):
return unicode(self.year_id)
class Order(models.Model):
user = models.ForeignKey(User, related_name='who_ordered')
select = models.ManyToManyField(Catalog, related_name='annuals_ordered', blank=True, null=True)
def __unicode__(self):
return unicode(self.user)
Here is the query I've been trying:
Catalog.objects.filter(order__select__annual='2014')

If you need users, you should start with users. Also, you need to filter on a specific field in Annual, ie year_id.
User.objects.filter(order__select__annual__year_id=2014)

If I got your question correctly then, your query is wrong. There is no attribute name order inside your Catalog model, then how can you use it for filtering ? Or I'm missing anything here ?
Directly using the related name references on the related fields, you can get the users by using -
# id is auto generated field or you can pass one annual_product object.
User.objects.filter(who_ordered__select__annual_products__id=1)
# OR
annual = Annual.objects.all()[0]
User.objects.filter(who_ordered__select__annual_products=annual)
The step by step how you can achieve the same :-
# where 1 is the id of an one object in Catalog model.
# You can pass Catalog object also to filter the users
Order.objects.filter(select__id=1)
# Here is the full query
catalog = Catalog.objects.all()[0]
orders = Order.objects.filter(select=catalog)
users = [o.user for o in orders] # This loop isn't necessary.
Now you have all orders specific to one Catalog, from this you can get the user object by using the user attribute in each order.

Related

Merge Django models into a view

I am attempting to merge and pull data from three Django models into a view. Players and Events relate to each other in the Details model (a player can attend many events) using models.ForeignKey.
In other platforms I would have written a DB View to join tables and the application would query that view.
From what I understand Django does not support data views within Models.
Looking for help on how I would approach this in Django.
class Players(models.Model):
firstName = models.CharField(max_length=255)
lastName = models.CharField(max_length=255)
highSchool = models.CharField(max_length=255)
gradYear = models.IntegerField()
slug = models.SlugField(default="", null=False)
class Events(models.Model):
title = models.CharField(max_length=255)
location = models.CharField(max_length=255)
date = models.DateField()
class Details(models.Model):
event = models.ForeignKey(Events, on_delete=models.CASCADE)
player = models.ForeignKey(Players, on_delete=models.CASCADE)
height = models.IntegerField(default=None, blank=True)
weight = models.IntegerField(default=None, blank=True)
def playerdetail(request,slug):
playerinfo = Details.objects.get(id=1)
template = loader.get_template('playerdetail.html')
context = {
'playerinfo': playerinfo,
}
return HttpResponse(template.render(context, request))
You are actually doing what you needed to do with the code you provided.
When you are invoking a query on a model that connects those two entities (Players,Events), it performs the join when you try to access each of these properties through the foreign key fields.
For example, for accessing the player information (which makes the Django ORM perform the join operation in the background):
# Get the first name of the player
first_name = playerinfo.player.firstName
For filtering and showing in other places, you can use the notation field__subfield
For more information, please read the examples of this website:
https://books.agiliq.com/projects/django-orm-cookbook/en/latest/index.html

Creating an "incomplete" Django class for a user to fill in

I have a database representing financial transactions. Columns representing payee and category are non-optional.
However, part of my app's functionality will be to ingest external spreadsheets of transactions which do not already have payee and category information. I would then populate a form where the user will select correct payees and categories through drop-down menus, and then save the completed information to the database.
Is the correct approach to simply create two separate but equivalent classes (see below)? Or is there some way to make one a sub-class to another, despite the fact that one is connected to a database and the other is not.
# An initial class representing a transaction read from an Excel sheet
# Payee and category information are missing at this stage, but will be filled
# in by the user later on
class TransactionFromSpreadsheet:
def __init__(self, date, amount):
self.date = date
self.amount = amount
self.payee = None
self.category = None
# The Django class that will be instantiated once all the user has completed all
# necessary information
class Transaction(models.Model):
date = models.DateField()
amount = models.DecimalField(max_digits=14, decimal_places=2)
category = models.ForeignKey('Category', on_delete=models.CASCADE)
payee = models.ForeignKey('Payee', on_delete=models.CASCADE)
One could use optional foreign keys and a custom manager to provide an easy way to query the "incomplete" or "complete" transactions.
class TransactionQueryset(models.query.QuerySet):
def complete(self):
return self.filter(category__isnull=False,
payee__isnull=False)
def incomplete(self):
return self.filter(category__isnull=True,
payee__isnull=True)
class TransactionManager(models.Manager):
def get_queryset(self):
return TransactionQueryset(self.model, using=self._db)
def complete(self):
return self.get_queryset().complete()
def incomplete(self):
return self.get_queryset().incomplete()
class Transaction(models.Model):
date = models.DateField()
amount = models.DecimalField(max_digits=14, decimal_places=2)
category = models.ForeignKey('Category', on_delete=models.CASCADE,
blank=True, null=True)
payee = models.ForeignKey('Payee', on_delete=models.CASCADE,
blank=True, null=True)
objects = TransactionManager()
And if you now need an incomplete transaction you could easily get these in a view:
def view_incomplete(request):
incompletes = Transaction.objects.incomplete()
return render(request, 'incomplete_template.html',
{'incompletes': incompletes})
It is now very comfortable to gather all heavily used filter conditions in the queryset and manager class.
And if you have non complementary filter conditions you could even chain the manager functions.

Relationships in Django - how to filter data

I've looked at a lot of entries and I know how to filter simple relationships. Unfortunately, I'm stuck and I don't know how to filter my table data when one of the tables is a branch of a certain string.
models.py
from django.contrib.auth.models import User
class Autor(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, unique=True)
description = models.TextField(blank=True, null=True)
class Incident(models.Model):
group_no = models.ForeignKey(Group, on_delete=models.CASCADE, default=1)
description = models.TextField(blank=True, null=True)
deleted = models.BooleanField(default=False)
class Department-leader(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name="leader")
department = models.ForeignKey(Group, on_delete=models.CASCADE)
class Group(models.Model):
group_no = models.CharField(max_length=40, unique=True)
active = models.BooleanField(default=True)
views.py
def get_author(user):
qs = Autor.objects.filter(user=user)
if qs.exists():
return qs[0]
return None
def show_all(request):
show_all_records = Incident.objects.filter(deleted=False).order_by('-id')[:300]
if request.user.is_authenticated:
autors_list = get_author(request.user)
user_list = get_user_model()
logged_user = get_object_or_404(user_list, username__exact=autors_list)
(...)
print("Logged user: " + str(logged_user.id))
else:
logged_user = ""
context = {
'show_all_records': show_all_records,
'logged_user': logged_user,
}
return render(request, 'incident/all_records.html', context)
The show_all_records variable represents all the records of the Incident table and that is ok.
The second thing I would like to display are entries for the logged in person i.e. all incidents in particular departments of the leader who is logged in.
If the tables were connected linearly, I would have no problem building this filter.
But how to make a filter for this layout of tables?
In pure SQL, it would look something like this:
select
bledy_bledy.nr_zlecenia,
bledy_bledy.ilosc_bledow,
bledy_gruparobocza.nr_grupy,
auth_user.username,
auth_user.id
from
bledy_bledy
LEFT JOIN
bledy_lider_dzial
on
bledy_bledy.nr_grupy_roboczej_id = bledy_lider_dzial.dzial_id
LEFT JOIN
bledy_gruparobocza
on
bledy_lider_dzial.dzial_id = bledy_gruparobocza.id
LEFT JOIN
auth_user
on
bledy_lider_dzial.user_id = auth_user.id
where
auth_user.id = 4
**Can I count on some hint on how to build it?**
I think this may be what you want.
user = request.user
# only need the primary keys of the group leaders for the next query
department_ids = user.leader.all().values_list('department_id', flat=True)
incidents = Incident.objects.filter( group_no_id__in= departments )
Also a suggestion: stick to Django conventions for naming foreign keys and related names. leaders not leader (because it refers to a plurality or set of leaders), and group not group_no because it refers to a Group object. (The actual FK/primary key value is obtained by appending _id to the field name, as used in the above. This is Django "magic".)

Django querysets - Performing subqueries

How to make query to get all components added by a certain user? Also in the resulting object I want to have the system name and project name too. I saw examples demonstrating sub-queries for two tables, but I need this for 3 tables.
Thank you.
class component(models.Model):
id = models.AutoField(primary_key=True)
server = models.ForeignKey(server, on_delete="CASCADE")
name = models.TextField(blank=True)
comments = models.TextField()
timestamp = models.DateTimeField(auto_now=True)
class system(models.Model):
id = models.AutoField(primary_key=True)
project = models.ForeignKey(project, on_delete="CASCADE")
name = models.CharField(max_length=255,blank=True)
comments = models.TextField()
timestamp = models.DateTimeField(auto_now=True)
class project(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=255,blank=True)
comments = models.TextField()
user = models.ForeignKey(User, on_delete="CASCADE")
timestamp = models.DateTimeField(auto_now=True)
No need to use subqueries here, we can simply perform joins, by looking "through" foreign keys (this is typically denoted with two consecutive underscores __ in Django).
If you want So all the Components that belong to a System that belongs to a Project that belongs to a user, we can query this with:
from django.db.models import F
Component.objects.filter(
system__project__user=someuser
).annotate(
system_name=F('system__name'),
project_name=F('system__project__name')
)
with someuser the user you want to filter with.
The components in this queryset will have extra attributes system_name and project_name that contain the name of the system and the project respectively.

Django Model Design foreign key relationships

I'm creating a model for ordering multiple items which I'd like to attach to a Catalog. I can't wrap my head around the correct way to do this. It makes sense to me to have the items foreign key to the Catalog and then the Order with a manytomany relationship with the Catalog. This returns the Catalog object itself in the order, not the Catalog items.
Here is my model:
class Article(models.Model):
catalog = models.ForeignKey(Catalog, related_name='article_products', blank=True, null=True)
title = models.CharField(max_length=200)
abstract = models.TextField(max_length=1000, blank=True)
full_text = models.TextField(blank=True)
def __unicode__(self):
return unicode(self.title)
class Catalog(models.Model):
pass
def __unicode__(self):
return unicode(self.id)
class Order(models.Model):
user = models.ForeignKey(User, related_name='who_ordered')
select = models.ManyToManyField(Catalog, related_name='annuals_ordered', blank=True, null=True)
def __unicode__(self):
return unicode(self.user)
I've attached an image from the admin: A few more items are attached in this image but it shows the issue. Instead of creating a new Catalog in Order, I'd like to be able to select from the items in Catalog to create a new Order.