Explaining based on the django tutorial for creating a library:
Say you allow a user to add a Book model to your library database. However you don't want them to add a Genre model to the Book. Instead, you have a Genre model in your database already with all listed Genre's in the world. When the user adds a Book to your database, you want your database to add the proper Genre to your Book based on text in the Book's summary. Say the Book has a TextField named summary and it has the words science and fiction in it when a bookinstance is created. The database will then add a Genre of science fiction to your bookinstance based on the words found in the bookinstance's summary TextField. Whether this happens at the moment of bookinstance creation or immediately after doesn't matter to me.
I am trying to do this same thing with a website that would handle logging/creating workouts. When a user adds a workout (i.e. book to the library) to the database, I would like the database to add a movement (i.e. genre for books) to the workout depending upon what text is in the workout TextField. Example below:
I add a Workout to the website with the following information as a TextField:
5 rounds of:
15 Pull Ups
30 Squats
200m Run
I then want the database to create the proper relationship between the workout instance and the movement models that are used in the workout instance's TextField: Pull Up, Squat, and Run.
This way I can search the database by workouts that contain pull ups, squats, or runs and this workout will show up. Each movement model also contains a 'Classification' ForeignKey of Upper Body, Lower Body etc. So searches can also be done for workouts that are Upper body only and so on.
My question is, how would I implement the model relationships between Workout, Movement, and Classification? I don't think I can use a ForeignKey or ManytoMany relationship between a workout and movement because they would both need to be added at the same time. In other words, the user would have to type the workout into the TextField and then also add which movements are in the workout. I want this to be a one step process for the user. They type in the workout, the database adds the relationship to which movements are in the workout based off what the user typed into the TextField. I assume in order for this functionality to work a relationship must be created between the workout and movement model after the workout model is created, not during. However, I'm not sure how to create this functionality on the database level.
I'm currently using SQLite as the database as it is the default with Django.
Any suggestions?
With this code your users will be able to choose movement and bodyPart by themselves:
from django.db import models
movement_tuple = (
('Pulls up', 'Pulls up'),
('Squats', 'Squats'),
('Run', 'Run')
)
body_part = (
('Upper Body', 'Upper Body'),
('Lower Body', 'Lower Body')
)
class Workout(models.Model):
name = models.CharField(max_length=50, default=None)
movement = models.CharField(max_length=30, choices=movement_tuple)
body_part = models.CharField(max_length=30, choices=body_part)
value = models.CharField(max_length=30)
def __str__(self):
return(self.name)
tuples are to make choice possible
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.
I am creating model class Car and I want to have in it two references to one foreign key.
class Car(models.Model):
owner = models.ForeignKey(User)
#and here I want to have owner email (which is already set in class User)
email = owner.email
But I don't know how to make reference to field of ForeignKey already used.
I get this error:
AttributeError: type object 'User' has no attribute 'email'
Is there any way to do it?
There are two things here... the first is to find out why you want to do this. Because maybe you shouldn't.
If you just want to access the owner's email address from a Car instance you don't need to add it as a field on the Car model, you can do:
my_car = Car.objects.get(owner=me)
my_email = my_car.owner.email
This does two seperate db queries, the first to get the Car and the second to get the owning User when you access the ForeignKey.
If you want to avoid this you can use select_related:
my_car = Car.objects.select_related().get(owner=me)
my_email = my_car.owner.email
Now it's only one query, Django knows to do a join in the underlying SQL.
But, assuming you know all this and you still really want to add the owner's email to the Car model. This is called 'denormalisation' and there can be valid performance reasons for doing it.
One problem that arises is how to keep the email address in sync between the User and Car models. If you are deliberately pursuing denormalisation in your Django app I highly recommend you consider using django-denorm. It installs triggers in the SQL db and provides a nice interface for specifying denormalised fields on your model.
You should really follow django's tutorial...
You can access the user email with car_instance.owner.email.
There is no need to add existing fields to another module. You should in principle avoid repeating data. Since the email and all relevant user info exist in the user model, then the foreign key is enough to access this data in relevance to a specific car record:
car = Car.objects.first()
email = car.owner.email
You can do the same with any field of the user model.
This might be a little far fetched, but I want to give it a try anyway.
I want to auto populate a m2m field when the user selects a foreignkey from the same model.
For example:
venue = models.ForeignKey(Instance)
kill = models.ManyToManyField(Boss)
Now, Boss has a relation to Instance already in Instance.instance what I want is when someone selects the venue say object #1 then the m2m field will automagically update to only boss objects from that instance.
I don't even know where to start with this, google has not really helped at all. Maybe the way I'm searching for it is wrong.
I am modeling a person like so:
class Person(models.Model):
""" Represent a person who has credentials. The person may have
devices and/or accessories. """
#basic information
name = models.CharField(max_length=50, unique=True)
email = models.CharField(max_length=50, unique=True)
phone_number = PhoneNumberField(unique=True)
But I want to be able to add something like:
/* create admin user with a group priveledge*/
user_account = #link to user account here
I'm doing this because as I add people objects, I want them to have an account with certain privelidges.
Thanks to anyone who can help..
It globally depends on the version of Django you're using, since User Models and Inheritance have changed with Django 1.5.
You might want to take a look at the Django Official Documentation : https://docs.djangoproject.com/en/dev/topics/auth/customizing/#extending-the-existing-user-model
Cheers,
K.
EDIT 19:30 CEST : You should be taking care of the switch in the right top of the Django's Documentation website, just to be sure you're looking at the right documentation (i.e. the documentation that concerns the Django's version you're using). As from 1.5, Django added an awesome thing : not just "relate" to the User model, but also Extends it. https://docs.djangoproject.com/en/dev/topics/auth/customizing/#substituting-a-custom-user-model .
And, as I guess that you want to add user via the automatic admin, you should take care of this paragraph too : https://docs.djangoproject.com/en/dev/topics/auth/customizing/#custom-users-and-django-contrib-admin
The only thing is that you must run Django 1.5+ to do that. Django 1.4 and lesser just let you add a related model, wich is not so bad yet :).
Can we say that what you want is just a User with a few more informations ?
Or is there a situation where you'll say
"A person may not be a user" and/or
"A user may not be a person"
What you describe here is possible with the simple system of the foreign key. Make a new model Person like this :
class Person(models.Model):
... some infos here ...
user = model.ForeignKey(User)
And then, you'll just have to create the User and then, the Person. There's many ways you can tell Django to create automatically the Person object when the User object is created. This blogpost could drive you the right way : http://www.turnkeylinux.org/blog/django-profile
Maybe you should follow it, and then explain us exactly what you miss ?
Cheers, again.
K.