Good evening,
I am working on some little website for fun and want users to be able to add items to their accounts. What I am struggling with is coming up with a proper solution how to implement this properly.
I thought about adding the User Object itself to the item's model via ForeignKey but wouldn't it be necessary to filter through all entries in the end to find the elements attached to x user? While this would work, it seems quite inefficient, especially when the database has grown to some point. What would be a better solution?
From what I understand of your use case, a User can have many items and and an Item can belong to multiple users. It this s the case, using ManyToManyField seems the way to go :
class Item(models.Model):
users = models.ManyToManyField('auth.User', related_name='items')
You can then query items from a specific user like this:
# adding an item to a user
user.items.add(my_item)
# query user items
user.items.all()
user.items.filter(name__startswith='Hello')
If you want to store additional information about the relationship, such as the date were the item was linked to the user, you have to specifiy an explicit intermediate model:
class Item(models.Model):
users = models.ManyToManyField('auth.User', through='ItemUser', related_name='users')
class ItemUser(models.Model):
"""Explicit intermediary model"""
user = models.ForeignKey('auth.User')
item = models.ForeignKey(Item)
date_added = models.DateTimeField(auto_now_add=True)
To create the binding beetween a User and an Item, just instanciate the intermediate model:
binding = ItemUser(user=user, item=item)
binding.save()
assert user in item.users.all()
You could create a model UserItems for each user with a ForeignKey pointing to the user and an item ID pointing to items. The UserItems model should store the unique item IDs of the items that belong to a user. This should scale better if items can be attached to multiple users or if items can exist that aren't attached to any user yet.
Related
Problem Description
Suppose I have a database with multiple models running with a Django front-end.
One of the tables in the Inventory. The inventory consists of entries with the following specifications:
class InventoryItem(models.Model):
item_name = models.TextField(max_length=10) #apple, orange, cilantro, etc...
item_quantity = models.DecimalField(...)
The next model will be to describe what is made with those ingredients
class Product(models.Model):
product_name = models.TextField(...)
product_description = models.TextField(...)
The ProductItem model also needs to keep track of the ingredients taken from inventory by specifying the InventoryItem and the quantity used from that inventory item used.
Previous Experience
In a previous experience, I have done something similar with EntityFramework in C# with MySQL. The way I achieved that was using another table/model called RecipeElement, where each one of those would be foreign-keyed to a ProductItem entry. The RecipeElement model looked like the following:
class RecipeElement(models.Model):
inventory_item = models.ForeignKey(InventoryItem, on_delete = models.CASCADE)
quantity_used = models.DecimalField(...)
product_item = models.ForeignKey(ProductItem, on_delete = models.CASCADE)
The Issue
My issue with that approach in Django is twofold:
How would I retrieve the RecipeElement entries associated with a ProductItem entry
How would the user input the RecipeElement entries and the ProductItem entries on one page. (The number of RecipeElements for each ProductItem is not limited, but each RecipeElement is associated with only one ProductItem
I am using SQLite for the moment but plan to transfer to MySQL in the future, if that changes anything.
If you want to retrieve all the RecipeElement for a Product do something like:
ProductItem.objects.get(pk=1).recipeelement_set.all()
In the second issue you can add a recipeElement from a product using .add() or create() like:
ProductItem.objects.get(pk=1).recipeelement_set.add(your_recipe_element)
I have a model which is an instance for the existence of an item (a ticket), and on each creation of a ticket I create a instance of another model, a record. Each record keeps track of who made a change to the ticket, and what they did with it, it basically keeps a record of what has happened with it. I want to tickets creator and creation date to be defined as the creator and creation date of the first activity made which points to it. (The first of the many in a many to one relation.
As is, I have a function which does this very simply:
def created_by(self):
records = Record.objects.filter(ticket=self.id).order_by('created_on')
return records[0].created_by
However I run into an issue with this when trying to sort a collection of tickets (which is logically most often going to be sorted by creation date). I cannot sort by a function using django's filter for queries.
I don't really want to store the redundant data in the database, and I'd rather have the record than not so all date items related to the ticket can be seen in the records. Idea's on how to make it so I can sort and search by this first instance of record? (Also need to search for the creator because some users can only see their own tickets, some can see all, some can see subsets)
Thanks!
Assuming the Record ticket field is a Foreign key to the Ticket model:
class Record (models.Model):
....
create_time = models.DateTimeField()
ticket = models.ForeignKey(Ticket,related_name='records')
You can replace the ModelManager (objects) of the Ticket model and override the get_queryset function:
class TicketManager(models.ModelManager):
def get_queryset():
return super(TicketManager, self).get_queryset().annotate(create_time=Min('records__create_time')).order_by('create_time')
class Ticket(models.Model):
.....
objects = TicketManager
Now every query like Ticket.objects.all() or Ticket.objects.filter(...) will be sorted by the create time
There must be a way to do this query through the ORM, but I'm not seeing it.
The Setup
Here's what I'm modelling: one Tenant can occupy multiple rooms and one User can own multiple rooms. So Rooms have an FK to Tenant and an FK to User. Rooms are also maintained by a (possibly distinct) User.
That is, I have these (simplified) models:
class Tenant(models.Model):
name = models.CharField(max_length=100)
class Room(models.Model):
owner = models.ForeignKey(User)
maintainer = models.ForeignKey(User)
tenant = models.ForeignKey(Tenant)
The Problem
Given a Tenant, I want the Users owning a room which they occupy.
The relevant SQL query would be:
SELECT auth_user.id, ...
FROM tenants_tenant, tenants_room, auth_user
WHERE tenants_tenant.id = tenants_room.tenant_id
AND tenants_room.owner_id = auth_user.id;
Getting any individual value off the related User objects can be done with, for example, my_tenant.rooms.values_list('owner__email', flat=True), but getting a full queryset of Users is tripping me up.
Normally one way to solve it would be to set up a ManyToMany field on my Tenant model pointing at User with TenantRoom as the 'through' model. That won't work in this case, though, because the TenantRoom model has a second (unrelated) ForeignKey to User(see "restictions"). Plus it seems like needless clutter on the Tenant model.
Doing my_tenant.rooms.values_list('user', flat=True) gets me close, but returns a ValuesListQuerySet of user IDs rather than a queryset of the actual User objects.
The Question
So: is there a way to get a queryset of the actual model instances, through the ORM, using just one query?
Edit
If there is, in fact, no way to do this directly in one query through the ORM, what is the best (some combination of most performant, most idiomatic, most readable, etc.) way to accomplish what I'm looking for? Here are the options I see:
Subselect
users = User.objects.filter(id__in=my_tenant.rooms.values_list('user'))
Subselect through Python (see Performance considerations for reasoning behind this)
user_ids = id__in=my_tenant.rooms.values_list('user')
users = User.objects.filter(id__in=list(user_ids))
Raw SQL:
User.objects.all("""SELECT auth_user.*
FROM tenants_tenant, tenants_room, auth_user
WHERE tenants_tenant.id = tenants_room.tenant_id
AND tenants_room.owner_id = auth_user.id""")
Others...?
The proper way to do this is with related_name:
class Tenant(models.Model):
name = models.CharField(max_length=100)
class Room(models.Model):
owner = models.ForeignKey(User, related_name='owns')
maintainer = models.ForeignKey(User, related_name='maintains')
tenant = models.ForeignKey(Tenant)
Then you can do this:
jrb = User.objects.create(username='jrb')
bill = User.objects.create(username='bill')
bob = models.Tenant.objects.create(name="Bob")
models.Room.objects.create(owner=jrb, maintainer=bill, tenant=bob)
User.objects.filter(owns__tenant=bob)
I have a query that gets me 32 avatar images from my avatar application:
newUserAv = Avatar.objects.filter(valid=True)[:32]
I'd like to combine this with a query to django's Auth user model, so I can get the last the last 32 people, who have avatar images, sorted by the date joined.
What is the best way to chain these two together?
The avatar application was a reusable app, and its model is:
image = models.ImageField(upload_to="avatars/%Y/%b/%d", storage=storage)
user = models.ForeignKey(User)
date = models.DateTimeField(auto_now_add=True)
valid = models.BooleanField()
Note that the date field, is the date the avatar is updated, so not suitable for my purporse
Either you put a field in your own User class (you might have to subclass User or compose with django.contrib.auth.models.User) that indicates that the User has an avatar. Than you can make your query easily.
Or do something like that:
from django.utils.itercompat import groupby
avatars = Avatar.objects.select_related("user").filter(valid=True).order_by("-user__date_joined")[:32]
grouped_users = groupby(avatars, lambda x: x.user)
user_list = []
for user, avatar_list in grouped_users:
user.avatar = list(avatar_list)[0]
user_list.append(user)
# user_list is now what you asked for in the first_place:
# a list of users with their avatars
This assumes that one user has one and only one avatar. Your model allows for more than one avatar per user so you have to watch out not to store more than one.
Explanation of Code Snippet:
The avatars of the most 32 recent joined users are requested together with the related user, so there doesn't have to be a database query for any of them in the upcoming code.
The list of avatars is then grouped with the user as a key. The list gets all items from the generator avatar_list and the first item (there should only be one) is assigned to user.avatar
Note that this is not necessary, you could always do something like:
for avatar in avatars:
user = avatar.user
But it might feel more naturally to access the avatars by user.avatar.
It's not possible to combine queries on two different base models. Django won't let you do this (it'll throw an error telling you exactly that).
However, if you have a foreignkey from one model to the other, then adding select_related() to your query will fetch the related objects into memory in a single DB query so that you can access them without going back to the DB.
Ok, I am working on a Django application with several different models, namely Accounts, Contacts, etc, each with a different set of fields. I need to be able to allow each of my users to define their own fields in addition to the existing fields. I have seen several different ways to implement this, from having a large number of CustomFields and just mapping a custom name to each field used by each user. I have also seem recommendations for implementing complex mapping or XML/JSON style storage/retrieval of user defined fields.
So my question is this, has anyone implemented user defined fields in a Django application? If so, how did you do it and what was your experience with the overall implementation (stability, performance, etc)?
Update: My goal is to allow each of my users to create n number of each record type (accounts, contacts, etc) and associate user defined data with each record. So for example, one of my users might want to associate an SSN with each of his contacts, so I would need to store that additional field for each Contact record he creates.
Thanks!
Mark
What if you were to use a ForeignKey?
This code (untested and for demo) is assuming there is a system-wide set of custom fields. To make it user-specific, you'd add a "user = models.ForiegnKey(User)" onto the class CustomField.
class Account(models.Model):
name = models.CharField(max_length=75)
# ...
def get_custom_fields(self):
return CustomField.objects.filter(content_type=ContentType.objects.get_for_model(Account))
custom_fields = property(get_fields)
class CustomField(models.Model):
"""
A field abstract -- it describe what the field is. There are one of these
for each custom field the user configures.
"""
name = models.CharField(max_length=75)
content_type = models.ForeignKey(ContentType)
class CustomFieldValueManager(models.Manager):
get_value_for_model_instance(self, model):
content_type = ContentType.objects.get_for_model(model)
return self.filter(model__content_type=content_type, model__object_id=model.pk)
class CustomFieldValue(models.Model):
"""
A field instance -- contains the actual data. There are many of these, for
each value that corresponds to a CustomField for a given model.
"""
field = models.ForeignKey(CustomField, related_name='instance')
value = models.CharField(max_length=255)
model = models.GenericForeignKey()
objects = CustomFieldValueManager()
# If you wanted to enumerate the custom fields and their values, it would look
# look like so:
account = Account.objects.get(pk=1)
for field in account.custom_fields:
print field.name, field.instance.objects.get_value_for_model_instance(account)