Django check if object in ManyToMany field - django

I have quite a simple problem to solve. I have Partner model which has >= 0 Users associated with it:
class Partner(models.Model):
name = models.CharField(db_index=True, max_length=255)
slug = models.SlugField(db_index=True)
user = models.ManyToManyField(User)
Now, if I have a User object and I have a Partner object, what is the most Pythonic way of checking if the User is associated with a Partner? I basically want a statement which returns True if the User is associated to the Partner.
I have tried:
users = Partner.objects.values_list('user', flat=True).filter(slug=requested_slug)
if request.user.pk in users:
# do some private stuff
This works but I have a feeling there is a better way. Additionally, would this be easy to roll into a decorator, baring in mind I need both a named parameter (slug) and a request object (user).

if user.partner_set.filter(slug=requested_slug).exists():
# do some private stuff

If we just need to know whether a user object is associated to a partner object, we could just do the following (as in this answer):
if user in partner.user.all():
#do something

Related

Django how to implement a one-to-many self-dependent foreign key

I am implementing a User referral system, which existing users can refer other people to register an account with the link they provided. After the new user registers, the new user will be stored to the field 'referred_who' of the existing user.
I have tried using the following method:
class CustomUser(AbstractBaseUser):
...
referred_who = models.ManyToManyField('self', blank=True, symmetrical=False)
class ReferralAward(View):
def get(self, request, *args, **kwargs):
referral_id = self.request.GET['referral_id']
current_referred = self.request.GET['referred']
// referrer
user = get_user_model().objects.filter(referral_id=referral_id)
// user being referred
referred_user = get_user_model().objects.filter(username=current_referred)
for item in user:
previous_referred = item.referred_who
previous_referred.add(referred_user[0])
user.update(referred_who=previous_referred)
And I got the following error:
Cannot update model field <django.db.models.fields.related.ManyToManyField: referred_who> (only non-relations and foreign keys permitted).
I am not sure if this method even works. I have check the Django Admin backend and I realized the 'Referred who' field actually contains all the users. It seems that it only highlightes the user being referred instead of only showing the referred users.
Also, I tried to access the 'referred_who' field in the back-end and it returns 'None'.
Is there a way to stored the users in the 'referred_who' field so that I can see all of the user being referred and access them in the back-end? For instance:
referral_id = self.request.GET['referral_id']
user = get_user_model().objects.filter(referral_id=referral_id)
print(user[0].referred_who)
Can someone show me a better way to do it? Thanks a lot!
You ask how to create a 1-Many field, but in your models you're trying to create m2m. Just change field to FK.
referred_who = models.ForeignKey('self', blank=True).
In case you need to have multiple fks to the same model, you need to specify related_name as well. You can use name of the field for it. More in docs.

allow unauthenticated users to suggest changes, but wait for administrator approval before changing object

Assume I have a very simple model:
class School(models.Model):
name = models.CharField(max_length = 100, unique=True)
I want to allow unauthenticated users to use a modelform to suggest changes to School objects, but I want to flag those changes as not yet being seen by an administrator. Once an administrator approves, I will then make the suggested change to the existing School object.
What is the best way to do this? Do I need to subclass the School class, perhaps calling it UpdateToSchool and allowing users make suggestions on this subclassed model rather than the target model itself?
Here is one way you can address this, to have a SuggestedSchoolEdits (or something like that) class that would hold attributes such as:
class SuggestedSchoolEdits(object):
school = models.ForeignKey(School) #You could use generic foreign key to extend this to any type - not just school
field = models.CharField(choices=<list of fields user can edit>)
value = models.TextField()
user = models.ForeignKey(User, null=True, blank=True) #if you want approval for logged in users too
moderator_approved = models.BooleanField()
approver = models.ForeignKey(User)
#Whatever else you wish to track
Now, when an edit is made, in the view, you can create an object of this type instead of updating the existing object. Once a moderator approves, a post_save signal could trigger the update of the School object.
This way, you have complete control over which one gets approved, rejected, etc. and you can keep track of suggestions, etc..

django model attribute field empty list

I'm trying to build an online forum. Right now, my forum model has several attributes and one of it is "owner", which is a ForeignKey to the user that created this forum. It also has another attribute "passcode" which makes sure that whenever an owner creates a forum, he/she has to type a passcode so that only others with the right passcode can join the forum. Now, I am trying to implement a new function such that users can choose to join existing forums; however, I am stuck.
1) My first issue is that in order to create a custom permission, I first need another model attribute that contains a list of the permissioned users. I was thinking of having a model attribute as an empty list, permissioned_users = [], so that whenever a user requests to join a forum and has the right passcode, his/her username will be appended to the list and then in my views.py I can use #user_passes_test to check if the request.user.username is in the list. However, i'm not sure if "students = []" will work such that i can do "anyparticularinstance".students.append("his name") will work.
2) How do i create a join forum function? I have fully implemented a create forum function but how do I allow users to join an existing forum? Thank you!
One Way you can achieve the permissions is by defining a Boolean field in your model, for example:
class Forum(AbstractBaseUser):
username=models.CharField(max_length=20,unique=True)
name = models.CharField(max_length=20)
email = models.EmailField(max_length=254,null=True,blank=True)
is_active = models.BooleanField(default=True)
is_admin = models.BooleanField(default=False)
is_staff = models.BooleanField(default=False)
By extending the AbstractBaseUser in Django you can define custom permissions for users.Either from the default Admin provided by Django or may be your own custom admin, you can add or remove permissions to a particular user. For more information you can see the following link Django AbstractBaseUser
You can achieve your objective by using textfield, and then appending at the end of the textfield everytime you update the field.
Model:
permissioned_users = models.TextField(blank=True, null=True)
View:
foo = Forum.objects.get(id=id)
temp = foo.permissioned_users
temp = temp+" username"
foo.permissioned_users = temp
foo.save()
Obviously you have to do some more work, ex. when you want to check which user is given permission, you split the string using whitespace hence str.split(), then you can easily iterate through it and make your checks.

Django making sure user and user profile have same pk

Right now I'm using Django's built in admin system to manage users, to which I've attached a profile to contain additional data using the following:
class Profile(models.Model):
user = models.OneToOneField(User, editable = False)
# Data fields here...
As it stands the User and Profile pk (and accordingly id number) will be the same if and only if the profile is created right after the user is created. I could guarantee that this would be the case during the registration process, and while that would cover most uses, creating users with the admin interface could cause mismatched ids to occur. Thus this does not seem like a very robust way to solve this problem and I'd like to hardcode the pk's to be the same. I'm not sure how to do this.
I thought the following would work:
profile_id = models.IntegerField(default=user.pk, editable = False,
primary_key = True)
But it gives me the error:
AttributeError: 'OneToOneField' has no attribute 'pk'
What's the best way to guarantee that the profile and user have the same pk? Note: I'd really rather not deal with extending the base user model as using the OneToOneField to link the two seems to be sufficient for all my needs.
Thanks!
[edit]
My reasoning for asking the question:
My immediate problem was that I wanted a dictionary of values of the User's Profile, which I was retrieving usingprofile_values = Profile.objects.filter(pk=user.id).values()[0]. This highlighted the bug, and I "hacked" around it last night using pk=user.profile.id instead. In the light of the morning this does not seem like such a terrible hack. However, it seems like having pk discrepancies could lead to quiet and hard to catch bugs down the line, and thus forcing them to match up would be a Good Idea. But I'm new to Django so I'd entirely accept that it is, in fact, never a problem if you're writing your code correctly. That said, for almost academic reasons, I'd be curious to see how this might be solved.
[/edit]
Like you already agree that it was never a problem because we have a OneToOne mapping between the two models.
So when you need to get the profile obj corresponding to a User:
profile_values = Profile.objects.get(user_id=user)
assuming,
class Profile(models.Model):
user = models.OneToOneField(User)
...
If your column name is not user, then use the corresponding name in get query.
Still if you are curious as to how to achieve same pk for both models, then we can set a signal on every save of User model. See the documentation.
def create_profile(sender, **kwargs):
if kwargs["created"]:
p = Profile(user=kwargs["instance"], ...)
p.save()
django.db.models.signals.post_save.connect(create_profile, sender=User)
create_profile() will be called every time any User object is saved.
In this function, we create Profile object only if a new User instance has been created.
If we start from blank slate, then I think this will always make sure that a Profile exists for every User and is created right after User was created; which in turn will give same pk for both models.
pk is a parameter in a filter() query, but not a field name. You probably want to use user.id.

Obfuscated Django URL for model objects

I have a Django model that looks something like this:
class Person(models.Model):
name = models.CharField(max_length=32)
place = models.ForeignKey(Place, related_name='people')
approved = models.BooleanField()
objects = PersonManager()
#models.permalink
def get_absolute_url(self):
return('deal_details', (), {
'person_slug': slugify(self.name),
})
As you could see, I already have an absolute URL for the object. However, I want to create a difficult-to-guess URL to keep track of the approval process of the object. Anyone done something similar and/or have any suggestions on how I should proceed?
My first thought was creating a model field like obfuscated_key that is generated randomly via the save function of the model. Then the URL would look something like /people/status/<id>/<obfuscated_key>/. But perhaps there's a better way to go about this?
A good way to do this would be to hash the object's ID with the installation's secret key (from settings.py). This is what the password reset email form does - there's some useful code in django.contrib.auth.tokens - and, in the very latest SVN versions, django.contrib.auth.crypto.
Something like a URL shortener (read: obfuscating) might work.
http://djangosnippets.org/snippets/1323/
I've used UUIDField to do something similar.
In the model:
uuid = UUIDField(auto=True)
then in the view check the id and uuid:
item = get_object_or_404(Item, id=id, uuid__exact=uuid)
The UUIDField is from http://djangosnippets.org/snippets/335/