Django manytomany: adding multiple, non-unique relationships? - python

I'm building an app that puts together the hardware of a computer. This is my first time working with django. Say I have the following models:
class Memory(models.Model):
partNum = models.CharField()
capacity = models.CharField()
class Computer(models.Model):
name = models.CharField()
memory = models.ManyToManyField(Memory)
# also has cpus, hard drives, and motherboard, but focus on memory for now
One memory object can belong to many computer objects, and one computer object can have many memory objects - hense Many-To-Many. However, computers require the same exact memory sticks installed if using more than one.
Yet django's manytomany field (by default?) only allows one instance of a memory-computer relationship, it must me unique. Any way around this?
If I try, in the admin page, to add many of the same memory objects to a computer, it says "Computer-memory relationship with this Computer and Memory already exists". If I try adding more than once the same memory object to a server object in the manage.py shell, it appears that only one memory object was added. If I try to manually edit the database to have a duplicate entry, I get an error saying that the entry already exists. I see that in the database structure, some sorta "unique together" index thingy is enforcing is enforcing this. If I altered the table to remove that clause, would that solve my problem? Probably not unless the django manager is more stupid than expected.
What are my options? Write my own intermediary models and use the through construct? But then I won't get to use the cool filter_horizontal widget! Rewrite my Computer model to have a Foreign Key field plus a field for the number of memory object? But then I won't get the ManyToMany API facilities. Help!

Edit: sorry, I did not read your post well enough, about not wanting to use 'through'.
One way to circumvent the problem, would be to use the "through" parameter, in which you can manually specify an intermediate model to use for the many-to-many relationship. In this way, you should still have (most of) the many-to-many facilities that Django provides.
The intermediate model could then have count (which I would find easier to manage than having multiple relations):
class Memory(models.Model):
partNum = models.CharField()
capacity = models.CharField()
class Computer(models.Model):
name = models.CharField()
memory = models.ManyToManyField(Memory, through='ComputerMemory')
class ComputerMemory(models.Model):
memory = models.ForeignKey(Memory)
computer = models.ForeignKey(Computer)
count = models.IntegerField()
For further information, take a look in the Django documentation: https://docs.djangoproject.com/en/dev/topics/db/models/#intermediary-manytomany

No this is not a compromise, Even if you dont create another table for the through thing, than also django will create it to remember exactly which memory is associated with each computer, so better is that you do it yourself...and this also allows you to get other fields in there that are required for a specific computer with a specific memory

Related

Abstract base class model vs Proxy model in Django

I am building a control panel that will have multiple sub-applications in Django. One of my models is an application, which will have important settings like name, description, install_path and id (so that I can associate specific settings and configuration values to this application.
Right now I'm struggling with trying to figure out how to declare this particular model. Each application will do something completely different than each other application. One might manage specific CMS settings and another may handle password resets for our development environment. The goal is to get the common support items in one place.
The main information for each application will be the same. Each will have a name, description, etc. The difference will be what they do and what settings they use. The settings are in their own model though, with a link back to the application via foreign key.
I'm unsure which model type would be most appropriate for my use case. Both look like they'd be useful, but if that's the case, I'm assuming that I am missing an aspect of one (or both) of them.
My question is, what is the difference between declaring my applications using abstract base class models vs. proxy models?
Nobody's touched this for 8 months. I should know better, but I'm going to take a stab at it.
Your first option, obviously, is to not use base classes at all and duplicate your Fields on each model. I know you didn't ask about this, but for others looking at this post, it is a good way to go for beginners. It's easy, and everything for the model is listed in one place rather than pointing to another model located somewhere else in the code for some of your fields.
Abstract base classes are probably the next easiest and next most commonly used. When you have a lot of duplication of fields across two or more models it is worth considering. Using this method you can eliminate the need to type (or cut and paste) fields over and over across multiple models. When you declare the base class abstract, the table is never actually built in the database. The base class is only used when the child tables are built. This keeps your database simpler and maintains performance because you don't have to build relationships to the base class and use joins to query data. You can also add additional fields (attributes) to the child classes on each of your child models(which proxy models cannot).
Proxy models are somewhat similar in that you have a base or parent class, but there are significant differences from there. You will use proxy models in situations where all of you models have the same fields (attributes), but you might have different "types" of objects. For instance you might have a base class of Cars, and use the manufacturer as your type. Then you may have Ford, Chevy and Honda models that are all proxy models of Cars. They all have the same fields. The manager class chosen for the model is what really makes them different from each other. From a database perspective, really only one table is built... Cars, leading to better performance than building multiple tables, but the drawback is you can't add manufactures-specific fields to the model without adding them to the entire Cars table.
In general I would recommend starting with Abstract Base Classes for models with lots of duplicate fields. Proxy models seem to be a more specific use case, but can be used as well if you have the use case and once you're more well-versed.
I'm not 100% clear on your specific use case based on your description, but hopefully I've given you enough information to decide what's best on your own.

Django set privacy options per model field

I have gone through the question, best way to implement privacy on each field in model django and Its answers doesn't seem solve my problem so I am asking some what related question here,
well, I have a User model. I want the user to make possible to control the privacy of each and every field of their profile (may be gender, education, interests etc . ..).
The privacy options must not to be limited to just private or public, but as descriptive as
public
friends
only me
friend List 1 (User.friendlist.one)
friend List 2 (User.friendlist.two)
friend List 3 (User.friendlist.three)
another infinte lists that user may create.
I also don't want these privacy options to be saved on another model, but the same so that with one query I could get the user object along with the privacy options.
so If I have the UserModel,
class User(models.Model):
name = models.CharField()
email = models.EmailField()
phone = models.CharField()
How do I setup a privacy setting here? I am using postgres, can I map a JSON field or Hstore even an ArrayField?
what is the best solution that people used to do with Django with same problem?
update:
I have n model fields. What I really want is to store the privacy settings of each instance on itself or some other convenient way.
I have worked on my issue, tried solutions with permissions and other relations. I have a Relationship Model and all other relationship lists are derived from the Relationship model, so I don't want to maintain a separate list of Relationships.
So my pick was to go with a Postgres JSONField or HStoreField. Since Django has good support for postgres freatures, I found these points pro for the choice I made.
JSON/HashStore can be queried with Django ORM.
The configurations are plain JSON/HashStore which are easy to edit and maintain than permissions and relations.
I found database query time taken are larger with permissions than with JSON/HStore. (hits are higher with permissions)
Adding and validating permissions per field are complex than adding/validating JSON.
At some point in future if comes a more simple or hassle free solution, I can migrate to it having whole configuration at a single field.
So My choice was to go with a configuration model.
class UserConfiguration(models.Model):
user = # link to the user model
configuration = #either an HStore of JSONFeild
Then wrote a validator to make sure configuration data model is not messed up while saving and updating. I grouped up the fields to minimize the validation fields. Then wrote a simple parser that takes the users and finds the relationship between them, then maps with the configuration to return the allowed field data (logged at 2-4ms in an unoptimized implementation, which is enough for now). (With permission's I would need a separate list of friends to be maintained and should update all the group permissions on updation of privacy configuration, then I still have to validate the permissions and process it, which may take lesser time than this, but for the cost of complex system).
I think this method is scalable as well, as most of the processing is done in Python and database calls are cut down to the least as possible.
Update
I have skinned down database queries further. In the previous implementation the relations between users where iterated, which timed around 1-2ms, changing this implementation to .value_list('relations', flat=True) cut down the query time to 400-520µs.
I also don't want these privacy options to be saved on another model, but the same so that with one query I could get the user object along with the privacy options.
I would advice you to decouple the privacy objects from the UserModel, to not mess your users data together with those options. To minimize the amount of database queries, use djangos select_related and prefetch_related.
The requirements you have defined IMO lead to a set of privacy related objects, which are bound to the UserModel. django.contrib.auth is a good point to start with in this case. It is build to be extendable. Read the docs on that topic.
If you expect a large amount of users and therefore also an even larger amount of groups you might want to consider writing the permissions resolved for one user in a redis based session to be able to fetch them quickly on each page load.
UPDATE:
I thought a little more about your requirements and came to the conclusion that you need per object permission as implemented in django-guardian. You should start reading their samples and code first. They build that on top of django.contrib.auth but without depending on it, which makes it also usable with custom implementations that follow the interfaces in django.contrib.auth.
What about something like this?
class EditorList(models.Model):
name = models.CharField(...)
user = models.ForeignKey(User)
editor = models.ManyToManyField(User)
class UserPermission(models.Model):
user = models.ForeignKey(User)
name = models.BooleanField(default=False)
email = models.BooleanField(default=False)
phone = models.BooleanField(default=False)
...
editor = models.ManyToManyField(User)
editor_list = models.ManyToManyField(EditorList)
If a user wants to give 'email' permissions to public, then she creates a UserPermission with editor=None and editor_list=None and email=True.
If she wants to allow user 'rivadiz' to edit her email, then she creates a UserPermission with editor='rivadiz' and email=True.
If she wants to create a list of friends that can edit her phone, then she creates and populates an EditorList called 'my_friends', then creates a UserPermission with editor_list='my_friends' and phone=True
You should then be able to query all the users that have permission to edit any field on any user.
You could define some properties in the User model for easily checking which fields are editable, given a User and an editor.
You would first need to get all the EditorLists an editor belonged to, then do something like
perms = UserPermissions.objects.filter(user=self).filter(Q(editor=editor) | Q(editor_list=editor_list))
First of all, in my opinion you should go for multiple models and for making the queries faster, as already mentioned in other answers, you can use caching or select_related or prefetch_related as per your usecase.
So here is my proposed solution:
User model
class User(models.Model):
name = models.CharField()
email = models.EmailField()
phone = models.CharField()
...
public_allowed_read_fields = ArrayField(models.IntegerField())
friends_allowed_read_fields = ArrayField(models.IntegerField())
me_allowed_read_fields = ArrayField(models.IntegerField())
friends = models.ManyToManyField(User)
part_of = models.ManyToManyField(Group, through=GroupPrivacy)
Group(friends list) model
class Group(models.Model):
name = models.CharField()
Through model
class GroupPrivacy(models.Model):
user = models.ForeignKey(User)
group = models.ForeignKey(Group)
allowed_read_fields = ArrayField(models.IntegerField())
User Model fields mapping to integers
USER_FIELDS_MAPPING = (
(1, User._meta.get_field('name')),
(2, User._meta.get_field('email')),
(3, User._meta.get_field('phone')),
...
)
HOW DOES THIS HELPS??
for each of public, friends and me, you can have a field in the User model itself as already mentioned above i.e. public_allowed_read_fields, friends_allowed_read_fields and me_allowed_read_fields respectively. Each of this field will contain a list of integers mapped to the ones inside USER_FIELDS_MAPPING(explained in detail below)
for friend_list_1, you will have group named friend_list_1. Now the point is the user wants to show or hide a specific set of fields to this friends list. That's where the through model, GroupPrivacy comes into the play. Using this through model you define a M2M relation between a user and a group with some additional properties which are unique to this relation. In this GroupPrivacy model you can see allowed_read_fields field, it is used to store an array of integers corresponding to the ones in the USER_FIELDS_MAPPING. So lets say, for group friend_list_1 and user A, the allowed_read_fields = [1,2]. Now, if you map this to USER_FIELDS_MAPPING, you will know that user A wants to show only name and email to the friends in this list. Similarly different users in friend_list_1 group will have different values in allowed_read_fields for their corresponding GroupPrivacy model instance.
This will be similar for multiple groups.
This will be much more cumbersome without a separate permissions model. The fact that you can associate a given field of an individual user's profile with more than one friend list implies a Many to Many table, and you're better off just letting Django handle that for you.
I'm thinking something more like:
class Visibility(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
field = models.CharField(max_length=32)
public = models.BooleanField(default=False)
friends = models.BooleanField(default=False)
lists = models.ManyToManyField(FriendList)
#staticmethod
def visible_profile(request_user, profile_user):
"""Get a dictionary of profile_user's profile, as
should be visible to request_user..."""
(I'll leave the details of such a method as an exercise, but it's not
too complex.)
I'll caution that the UI involved for a user to set those permissions is likely to be a challenge because of the many-to-many connection to friend lists. Not impossible, definitely, but a little tedious.
A key advantage of the M2M table here is that it'll be self-maintaining if the user or any friend list is removed -- with one exception. The idea in this scheme is that without any Visibility records, all data is private (to allow everyone to see your name, you'd add a Visibility record with user=(yourself), field="name", and public=True. Since a Visibility record where public=False, friends=False, and lists=[] is pointless, I'd check for that situation after the user edits it and remove that record entirely.
Another valid strategy is to have two special FriendList records: one for "public", and one for "all friends". This simplifies the Visibility model quite a bit at the expense of a little more code elsewhere.

When to use one-to-one relationships in Django models

I've read a few places (see the second answer) that one-to-one relationships in Django models should almost always only be used for inheritance, or to access an otherwise inaccessible model (like the Django user model).
However, it seems like there are cases where you have an object that will always have exactly one instance of another object where you would logically want to separate those two objects. Say, for example, your app was storing information about cars. Each car has exactly one driver and each driver only drives one car. Does it not make sense to separate car and driver into two separate models?
Imagine you have a company and make intranet tool listing all employees, their positions, offices, departments, salaries, etc. You would make in Django models.py a class Employee, maybe something like this:
class Employee(models.Model):
first_name = models.CharField(max_length=255)
last_name = models.CharField(max_length=255)
position = models.CharField(max_length=255)
office = models.CharField(max_length=20)
salary = models.IntegerField()
department = models.ForeignKey(Department, related_name='employees')
But for some reasons you don't want the salary to be accessible to all employees, maybe there are many people with redactor status in the admin area, and decide to outsource it to its own model and change the model Employee:
class Employee(models.Model):
# above attributes
salary = models.OneToOneField(Salary)
There are certainly other ways to hide this information, but one possibility is to divide the information in two tables even though it is just plain 1:1 relation.
Your company is a software company and you introduce pair programming. Every employee has a pair programming partner. It can be just one programming partner. So you adapt your model again:
class Employee(models.Model):
# above attributes
pair_programmer = models.OneToOneField('self')
This would be a recursive one-to-one relation.
One-to-one relations are not very common and hard to find in beginners' tutorials, but if there are some specific requirements, you find yourself creating 1:1 relation for solving the problem.
Here is a real life example from my work. I'm bioinformatician and make software for microorganisms. They are classed in genera and species. Every genus may contain one or more species, but a species can belong to only one genus. That is a clear 1:n relation. But now, a genus has a type species, only one and just one. And the type species can belong to one genus. Here I put models.OneToOneField, besides the models.ForeignKey.
Don't worry a lot about 1:1 relations in advance. When you come to some specific problem, then you'll figure out if you need 1:1 relation.
The django documentation gives a great answer:
For example, if you were building a database of “places”, you would
build pretty standard stuff such as address, phone number, etc. in the
database. Then, if you wanted to build a database of restaurants on
top of the places, instead of repeating yourself and replicating those
fields in the Restaurant model, you could make Restaurant have a
OneToOneField to Place (because a restaurant “is a” place; in fact, to
handle this you’d typically use inherit Each car has exactly one driver and each driver only drives one car. Does it not make sense to separate car anance, which involves an
implicit one-to-one relation).
Django uses OneToOne to model inheritance (and might use it internally, I haven't checked out the source though). I feel like if there is a tool django is providing and you can use that tool in a way that you can defend, why not use it? It seems like it makes sense, that if a car only has one driver, then enforce that in the db using the tools (OneToOneField) that django provides

Django data modeling: Foreignkey or hard coded?

Suppose I have a table of animals which has two attributes name and type, whereas type can be: 'dog', 'cat', etc. Here are two ways to implement this in Django: one where type is a ForeignKey to AnimalType:
class Animal(models.Model):
name = models.CharField(max_length=10)
type = models.ForeignKey(AnimalType)
The other is to just have type as a predefined choice which would be defined in the imported module:
class Animal(models.Model):
name = models.CharField(max_length=10)
type = models.CharField(
max_length=10,
choices=ANIMAL_TYPE_CHOICES
)
The latter (predefined choice) seems more efficient to me, since types will never be dynamically updated by user interaction and if a new type needs to be added it will be added by a developer, i.e. the code would be updated rather than the database.
However, I would like to know if this would be a good/acceptable practice? Or should I waste a separate database table for such a "static" entry and also pay with extra time caused by db accesses?
Thanks.
The first way has the advantage that you don't have to touch the code in order to add a new type of animal.
And of course, someone using your app neither.
Adding a new animal type is something trivial and, for instance, you shouldn´t be messing with a working code deployed on a production server for just add an animal type.
If you´re having problems due to your database is empty at start using the application and because of that you don't have any animal types, well, try Django fixtures: Providing initial data for models
I prefer second way.
If you don't need to edit types from admin panel and always will change it with changes in your code, you do not need to have ForeignKeys and separate table.
In case of ForeignKey, you will have additional integrity check on the database level.
It can be useful if you delete some type and do not want to leave it in DB, for example.
I prefer field choices due to performance reasons. Even if the potential choices increases, as long as the functionality is just a choice selection, there's no reason to create an extra table

How to add many to one relationship with model from external application in django

My django project uses django-helpdesk app.
This app has Ticket model.
My app got a Client model, which should have one to many relationship with ticket- so I could for example list all tickets concerning specific client.
Normally I would add models.ForeignKey(Client) to Ticket
But it's an external app and I don't want to modify it (future update problems etc.).
I wold have no problem with ManyToMany or OneToOne but don't know how to do it with ManyToOne (many tickets from external app to one Client from my app)
Even more hacky solution: You can do the following in the module level code after you Client class:
class Client(models.Model):
...
client = models.ForeignKey(Client, related_name='tickets')
client.contribute_to_class(Ticket, name='client')
I haven't fully tested it (I didn't do any actual database migrations), but the correct descriptors (ReverseSingleRelatedObjectDescriptor for Ticket and ForeignRelatedObjectsDescriptor for Client) get added to the class, and South recognizes the new fields. So far it seems to work just like a regular ForeignKey.
EDIT: Actually not even that hacky. This is exactly how Django sets up foreign keys across classes. It just reverses the process by adding the field when the reverse related class is built. It won't raise an error if any of the original fields on either model is shadowed. Just make sure you don't do that, as it could potentially break your code. Other than that, I don't think there should be any issues.
There are (at least) two ways to accomplish it:
More elegant solution: Use a TicketProfile class which has a one-to-one relation to Ticket, and put the Client foreign key into it.
Hacky solution: Use a many-to-many relation, and manually edit the automatically created table and make ticket_id unique.

Categories