Django set privacy options per model field - python

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.

Related

What is a good way to store guest user information that may be saved to a permanent user after registration in django?

I have a RESTful django webapp that allows users to take quizzes in a progressive system where the quizzes become increasingly difficult. Their progress is saved when they answer a question. I'm using django-rest-framework.
class User(AbstractUser):
pass
class IntervalsProfile(models.Model):
# Belongs to User model in 1-to-1 relationship
# If User is deleted, then the corresponding IntervalsProfile will be deleted as well
user = models.OneToOneField(settings.AUTH_USER_MODEL, related_name='profile', on_delete=models.CASCADE)
level = models.IntegerField(default=0)
current_level = models.IntegerField(default=0) # Level the user is currently viewing/on
I would like users to be able to play as a guest, and if they so choose, register in order to save their progress. I cannot figure out how to save this guest information or how to save it once they register.
I've searched thoroughly and all the solutions I see are either several years old or seem too cumbersome (such as adding a check for authentication in every view and having 2 cases for each view).
Any advice or guidance would be much appreciated.
if they so choose, register in order to save their progress
Subclass django's User model and add a new field called is_guest = BooleanField. (django has good documentation on how to do this already.)
Ask your users to input just a username when they visit for the first time. (or you can also assign them a random username using something like coolname).
Create a User instance for their chosen username with is_guest=True without any password and now you can save all the game information anyway corresponding to this user.
Tomorrow, when they choose to register, you can simply update the already existing User instance and mark is_guest=False.
EDIT:
If you don't want the added complexity of your own User model, you could also achieve the same without subclassing User model and just adding the is_guest column to the IntervalsProfile model

difference between ForeignKey and extending the User class/model in Django

Django: When extending User, better to use OneToOneField(User) or ForeignKey(User, unique=True)?
I went through this thread and found that ForeignKey(with unique=True) is better than OneToOneField, but what about extending the class itself, I.e. here is the example
class UserProfile(models.Model):
user = models.ForeignKey(User, unique=True)
# some additional fields
OR
class UserProfile(User):
# some additional fields
Difference between these two approaches and pros/cons and which one should I use?
EDIT:
I can use AbstractUser as well
from django.contrib.auth.models import AbstractUser
class User(AbstractUser):
phone_no = models.CharField(max_length=10, blank=True)
and then mentioned AUTH_USER_MODEL = 'myapp.User' in settings.py
main concern is, what approach should I use, extending the class or ForeignKey ?
Duplicates:
What's the difference between OneToOne and Subclassing a model in Django
Django Model Inheritance versus OneToOne field
MORE EDIT
Forget about ForeginKey or OneToOne, assume only one of these two exist, now compare that with extending/subclassing approach
First, it is good to know there currently are several options how to extend the Django user model. Each has its purpose (but there is some overlap as well). Django docs are a bit confusing as it seems from this there are two options, i.e. proxy or OneToOneField. However this relates to the existing user model, as further on in the docs is dealt with custom user models.
So in practice there are four (common) ways to deal with extending the user model:
Proxy model (no new databasefields, just to change user model behavior, i.e. new ordering, new methods, etc.).
OneToOneField (extra datafields needed within existing Djang user model).
Custom user model with AbstractBaseUser (extra datafields
needed, and specific requirements regarding authenticaton process,
e.g. using emailaddress als id token instead of username).
Custom user model with AbstractUser (extra datafields needed, no
change to authentication).
Implementing option 3 and 4 a fresh database migration is needed, so these are only suitable for new projects.
This is a good link for more detail on this. I think option 2 and 4 are closest as both only want to extend to add more datafields. Writer seems in favor of option 2, but when starting a new project option 4 seems easier to me. Somewhere in the comments writer mentions risk of not being able to upgrade to new Django versions for option 3 and 4. Seems far-fetched to me, but I can't tell really.
There is no better way to do, the thing is if you do extend AbstractUser you need to redefine some functions so it may be longer but you have more control on what you wanna do with your user.
Make a OneToOne field on django default user is faster and also allow you to add your own user custom fields but you can use directly User default field in your custom object, and your custom field on the user :
from django.contrib.auth.models import User
class Employee(models.Model):
user = models.OneToOneField(User)
department = models.CharField(max_length=100)
You can do :
>>> u = User.objects.get(username='fsmith')
>>> freds_department = u.employee.department
So it really depends on what you want to do. You can do your User for example if you want to take the mail adress as the identification token (it's a common exmaple but you can do much more things :p).
Here is a good explanation (I place it on user but you can read the whole page it's pretty interesting when you dive into User and authentication into Django).
Hope it help.
I am skeptical about the benefits of a unique FK verses one-to-one, you could achieve a similar thing in the admin by using fieldsets so I would prefer to have an explicit one-to-one field on the model, making the nature of the relation more obvious.
The duplicate questions you linked to aren't specific to the auth User model and discuss one-to-one vs model inheritance generally. Technically they are both the same (i.e. model inheritance uses a one-to-one field)
So ultimately the choice comes down to semantics: is your related model a 'subclass' of the other, or just a link to further related info?
In the case of auth User you would ask yourself then: are there some extra fields that should be present for all users (eg gender, facebook id etc)? or some fields you want to omit from the Django User model (eg to use unique email address as username)?
In this case the obvious choice is to extend AbstractUser. If you can't imagine specifying null=True on your user profile model you should consider extending AbstractUser.
On the other hand there may be some data that is more analogous to the old UserProfile model (have a look how things were in old versions of Django before extending AbstractUser was supported: https://docs.djangoproject.com/en/1.4/topics/auth/#storing-additional-information-about-users)
Perhaps for example you have different types of users who may or may not have certain extra sets of fields. In this case it may make sense to have a one-to-one link to one or more 'profile' models.

django model with two fields from another model

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.

Should I delete or set a variable to "deleted" in django/postgresql?

Specifically for my app, I have created this model in order to allow a user (the user_parent) to follow other users.
class Follow(models.Model):
user_parent = models.ForeignKey(User, unique=True, related_name="follow_set")
users_followed = models.ManyToManyField(User, related_name="follow_followed")
Whenever a user parent follows another user, the user being followed is added to the variable users_followed.
Right now I am trying to figure out how best to unfollow other users. Do I delete the user being followed from the users_followed variable or should I add another field to the model describing whether the user is still being followed or not?
Which is the most expensive action for the database to perform?
It would be the same in terms of expense, since it is a table update. So either approach should be fine

How to set up Django models with two types of users with very different attributes

Note: I've since asked this question again given the updates to Django's user model since version 1.5.
I'm rebuilding and making improvements to an already existing Django site and moving it over from Webfaction to Heroku, and from Amazon's SimpleDB to Heroku Postgres (though testing locally on Sqllite3 when developing). A lot of what I'm doing is moving over to use built-in Django functionality, like the Django admin, user authentication, etc.
Conceptually, the site has two kinds of users: Students and Businesses. The two types of users have completely different permissions and information stored about them. This is so much the case that in the original structure of the site, we set up the data model as follows:
Users
ID (primary_key)
Business_or_Student ('B' if business, 'S' if student)
email (unique)
password (hashed, obviously)
...
Students
ID (Foreignkey on Users)
<more information>
...
Businesses
ID (Foreignkey on Users)
<more information>
...
This worked pretty well for us, and we had the bare-bones user information in the Users table, and then any more detailed information in the Students and Businesses tables. Getting a user's full profile required something along this pseudocode:
def get_user_profile(id):
if Users(id=id).Business_or_Student = 'B':
return Businesses(id=id)
else:
return Students(id=id)
In moving over, I've found that Django's built-in User object has pretty limited functionality, and I've had to extend it with a UserProfile class I've created, and then had additional Student and Business tables. Given all of the patching I'm doing with this in the Django admin, and being relatively unfamiliar with Django models since I always did it differently, I'm not sure if this is the best way to go about it, or if I should just stick all of the information for businesses and students in the UserProfile table and just differentiate the two with different groups, or if there's even some way to do this all in the built-in User object.
Since businesses and students also have different interfaces, I'm seriously considering setting up the two as different apps within my Django project, and so separating their views, models, etc. entirely. That would look something like:
MyProject/
MyProject/ (project folder, Django 1.4)
mainsite/
students/
businesses/
One of my biggest concerns is with the Django Admin. In extending User, I already had to add the following code:
class UserProfileInline(admin.StackedInline):
model = UserProfile
can_delete = False
verbose_name_plural = 'profile'
class UserAdmin(UserAdmin):
inlines = (UserProfileInline, )
However, I would like the information for the Business or Student aspects of the user to show up in the Django admin when that User is pulled up, but the ForeignKey part of the model is in the Student and Business model since every Student/Business has a User but every User has only one Student or one Business object connected with it. I'm not sure how to add a conditional Inline for the Admin.
Question: Given this structure and these concerns, what is the best way to set up this site, particularly the data model?
This is not a complete solution, but it will give you an idea of where to start.
Create a UserProfile model in mainsite. This will hold any common attributes for both types of users. Relate it to the User model with a OneToOne(...) field.
Create two more models in each app, (student/business), Business and Student, which have OneToOne relationships each with UserProfile (or inherit from UserProfile). This will hold attributes specific to that type of users. Docs: Multitable inheritance / OneToOne Relationships
You may add a field in UserProfile to distinguish whether it is a business or student's profile.
Then, for content management:
Define the save() functions to automatically check for conflicts (e.g. There is an entry for both Business and Student associated with a UserProfile, or no entries).
Define the __unicode__() representations where necessary.
I hope I understood your problem... maybe this can work? You create a abstract CommonInfo class that is inherited in into the different Sub-classes (student and businesses)
class CommonUser(models.Model):
user = models.OneToOne(User)
<any other common fields>
class Meta:
abstract = True
class Student(CommonUser):
<whatever>
class Business(CommonUser):
<whatever>
In this case the models will be created in the DB with the base class fields in each table. Thus when you are working with Students you run a
students = Students.objects.get.all()
to get all your students including the common information.
Then for each student you do:
for student in students:
print student.user.username
The same goes for Business objects.
To get the student using a user:
student = Student.objects.get(user=id)
The username will be unique thus when creating a new Student or Business it will raise an exception if an existing username is being saved.
Forgot to add the link

Categories