How to authenticate if a user "owns" a model instance - python

I have the following models:
class UserProfile(models.Model):
user = models.OneToOneField(User)
class Site(models.Model):
user = models.ForeignKey(User)
site_name = models.CharField(max_length=128, blank=False, null=False)
class Team(models.Model):
site = models.ForeignKey(Site)
team_member_name = models.CharField(default='name', max_length=128, blank=False, null=False)
I have a view that passes a team_member id via the URL:
Urls.py:
url(r'^team/(?P<team_member_id>\d+)/$', 'team.views.home', name='team_view_team_member')
Views.py:
#login_required
def home(request, team_member_id=None):
team_member = Team.objects.get(id=team_member_id)
Note that there are many Team instances (i.e. lots of team members) which have the same Site_id.
How can I test if the request.user has the same site_id as any team_member returned? Is there a simple way to repeat this if I want to use it across multiple views?

Try this:
team_member = Team.objects.get(id=team_member_id)
if team_member.site.id == request.user.site_set.all().first().id:
print "same site"
else:
print "different site"

Hope this helps.
# Site for user in request
req_user_site = Site.objects.get(user=request.user) # Use filter if it will return more than one object
# Site for team member in url
sites = Site.objects.filter(team__pk=team_member_id)
# Then compare
if req_user_site in sites:
print "have the same"
else:
print "don't"

Related

Checking if relationship exists with query

I am trying to check whether or not a following relationship exists using a query. First, I get all of the followers the user has and then I check whether or not the user follows those followers. Here are my models:
class Following(models.Model):
target = models.ForeignKey('User', related_name='followers', on_delete=models.CASCADE, null=True)
follower = models.ForeignKey('User', related_name='targets', on_delete=models.CASCADE, null=True)
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return '{} is followed by {}'.format(self.target, self.follower)
class User(AbstractBaseUser):
username = models.CharField(max_length=15, unique=True)
email = models.EmailField(max_length=100, unique=True)
I am using the Django Rest-Framework so I go to the specific URL to get the information I need. After going to the URL, the output is expected. I get all the followers the user has.
views.py
class GetFollowersView(ListAPIView):
serializer_class = FollowingSerializer
def get_queryset(self):
requested_user = get_requested_user(self)
return User.objects.filter(targets__target=requested_user).order_by('-targets__created_at'). \
annotate(is_following=Count('followers__follower', filter=Q(followers__follower=requested_user), distinct=True))
def get_requested_user(self):
filter_kwargs = {'username': self.kwargs['username']}
return get_object_or_404(User.objects.all(), **filter_kwargs)
serializers.py
class FollowingSerializer(serializers.ModelSerializer):
is_following = serializers.IntegerField()
class Meta:
model = User
fields = ('id', 'username', 'follower_count', 'following_count', 'is_following')
However, the problem is in the is_following annotation. I'd like to see whether or not the user follows each specific follower. If they follow that follower, then is_following should be 1 if not, then it is a 0. I'm getting incorrect results in is_following is there a way I can check if the user follows each specific follower?
If you have Django Debug Toolbar installed and you check the query for your current filter/annotate, this is what it shows (for a single user)
SELECT "user"."id", "user"."username", "user"."email",
COUNT(DISTINCT T4."follower_id") AS "is_following" FROM "user"
INNER JOIN "following" ON ( "user"."id" = "following"."follower_id" )
LEFT OUTER JOIN "following" T4 ON ( "user"."id" = T4."target_id" )
WHERE "following"."target_id" = 4 GROUP BY "user"."id", "user"."username",
"user"."email", "following"."created_at" ORDER BY "following"."created_at"
DESC
However to get the count of the users the chosen user follows, you really want something like this
SELECT ue."id", ue."username", ue."email", COUNT(DISTINCT fe."target_id") AS
"is_following" FROM "user" u inner JOIN "following" fe ON ( u."id" =
fe."follower_id" ) inner join user ue on fe.target_id = ue.id and u.id = 4
GROUP BY ue."id", ue."username", ue."email"
I don't think it is possible to combine both the followers and the followee in the same query like you have done. You could possibly find the intersection and then proceed from there...Something like this..
def get_queryset(self):
username = self.request.query_params.get('username', None)
requested_user = models.User.objects.get(username=username)
following_me = models.User.objects.filter(targets__target=requested_user).order_by('-targets__created_at')
i_follow = models.User.objects.filter(followers__follower=requested_user).order_by('-followers__created_at')
common = following_me & i_follow
### Set is_following for common as 1, all others as 0.
#......
#......
return following_me
Why not use an M2M relationship? Seems like this could be simple:
from django.db import models
class User(models.Model):
name = models.CharField(max_length=200)
followers = models.ManyToManyField('User')
#property
def follower_count(self):
# How many people follow me
return len(self.followers)
#property
def followee_count(self):
# How many people I follow
return len(self.user_set.all())
And you can modify the get_queryset() to only find followers:
User.objects.filter(followers__contains=self.request.user)
Does this help?

Django-Rest: Serialising ListField() - '_TaggableManager' object is not iterable

My goal is to add tags to a post. I'm using latest taggit (Requirement: Django-Taggit https://github.com/alex/django-taggit ) and DRF.
Goal: To pop tags only from posted request. For each tag in the post request to call post.tags.add("tag1","tag2") to add these tags to the model.
Currently, my post serialiser is:
class PostSerializer(serializers.ModelSerializer):
tags = serializers.ListField(
child=serializers.CharField(min_length=0, max_length=30)
)
...
def create(self, validated_data):
pub.set_trace()
tags_data = validated_data.pop('tags') # Need to pop tags only
# to_be_tagged, validated_data = self._pop_tags(validated_data)
images_data = self.context.get('view').request.FILES
post = Post.objects.create(**validated_data)
post.tags.add(*tags_data)
for image_data in images_data.values():
PostImage.objects.create(post=post, image=image_data)
return post
When I send a post request with the following data:
data = { 'title': 'First Post',
...
'tags':'["tag1"]'}
I get an error:
Exception Value: '_TaggableManager' object is not iterable
I also tried sending 'tags':'one, two, three' and simply one
Edit - PDB output:
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.
> /Users/gr/Desktop/PycharmProjects/godo/api/serializers.py(112)create()
-> tags_data = validated_data.pop('tags')
(Pdb) tags_data
*** NameError: name 'tags_data' is not defined
(Pdb)
Models.py
from taggit.managers import TaggableManager
class Post(models.Model):
objects = LocationManager() # to sort by distance
tags = TaggableManager()
created = models.DateTimeField(auto_now_add=True)
title = models.CharField(max_length=100, blank=False)
description = models.TextField(max_length=500, blank=False)
def __str__(self):
return '{}'.format(self.title)
Are you sure about the content of tags_data variable? Could you put there traceback and debug it (see: using (i)pdb for debugging)?
I think wrong is these line:
post.tags.add(tags_data)
lets try:
post.tags.add(*tags_data)
The star unpack list. See these answer for reference: https://stackoverflow.com/a/4959580/953553
In my project I ma handling tags which are in model M2M and in serializer I decided to use this approach:
tags = serializers.SlugRelatedField(
many=True,
queryset=Tag.objects.all(),
slug_field='text'
)
my models:
class Spot(models.Model):
tags = models.ManyToManyField(Tag, related_name='spot_facilities', null=True, blank=True)
class Tag(models.Model):
text = models.CharField(max_length=100, unique=True)

Django: 'Thoughts' object has no attribute 'choice_set' - Trouble accessing a choice set

In my model I define a Thoughts and Comments model. One thought has many Comments as so:
class Thoughts(models.Model):
name = models.CharField(max_length=30)
thought = models.CharField(max_length=500)
class Comments(models.Model):
name = models.CharField(max_length=30)
comment = models.CharField(max_length=200)
original_post = models.ForeignKey(Thoughts, default=0)
On my site, when you go to view a thought, I want all of the comments to appear. It is my understanding that you can use choice_set to access attributes via one-to-many relationship. Here's my view:
def thought(request, thought_num):
if request.method == 'POST':
form = CommentForm(request.POST)
if form.is_valid():
c = Comments.objects.create(name=form.cleaned_data['name'],
comment=form.cleaned_data['comment'])
c.save()
else:
form = CommentForm()
get_post = Thoughts.objects.get(pk=thought_num)
comments = get_post.choice_set.all()
return render(request, 'thought.html', {'form': form, 'comment':comments,})
In these lines, I attempt to access all comments related to a particular thought in order to print them in my template.
get_post = Thoughts.objects.get(pk=thought_num)
comments = get_post.choice_set.all()
When I access the page that should display the comments, I get this error:
Exception Type: AttributeError
Exception Value:'Thoughts' object has no attribute 'choice_set'
Perhaps I am missing something, I am not sure. I'm sure it's something simple. Thanks for your time
To retrieve all the Comments related to a Thought. You can do the following:
Thoughts.objects.get(pk=thought_num).comments_set.all()
If you would like to override the default related_name ("comments_set"). You can do the following:
original_post = models.ForeignKey(Thoughts, default=0, related_name='choice_set')
When you make a ForeignKey the default related name becomes the lower case name of the current class + "_set" so for your project should be:
get_post = Thoughts.objects.get(pk=thought_num)
comments = get_post.comments_set.all()
Or you could even create a custom related name instead of the default:
class Thoughts(models.Model):
name = models.CharField(max_length=30)
thought = models.CharField(max_length=500)
class Thoughts(models.Model):
name = models.CharField(max_length=30)
thought = models.CharField(max_length=500)
class Comments(models.Model):
name = models.CharField(max_length=30)
comment = models.CharField(max_length=200)
original_post = models.ForeignKey(Thoughts, default=0, related_name='comments')
so you can get your comments like this:
get_post = Thoughts.objects.get(pk=thought_num)
comments = get_post.comments.all()

how to save Many to Many relationship in django

How to create an object for a Django model with a many to many field?
From above question i come to know we can save Many to Many field later only.
models.py
class Store(models.Model):
name = models.CharField(max_length=100)
class Foo(models.Model):
file = models.FileField(upload_to='')
store = models.ManyToManyField(Store, null=True, blank=True)
views.py
new_track.file = request.FILES['file']
new_track.save()
And file uploading working fine then later i modify my code to add store then i am here...
Now i am sure db return id's here. Then i tried with my below code but that's given me error only
x = new_track.id
new = Foo.objects.filter(id=x)
new.store.id = request.POST['store']
new.save()
ok so the error here is 'QuerySet' object has no attribute 'store'
And also i tried with add that's now working either.
So the question is how to save()
the right way of saving objects with manytomany relations would be:
...
new_track.file = request.FILES['file']
new_track.save()
new_store = Store.objects.get(id=int(request.POST['store']))
new_track.store.add(new_store)
As of 2020, here's my approach to saving ManyToMany Field to a given object.
Short Answer
class HostingRequestView(View):
def post(self, request, *args, **kwargs):
form = VideoGameForm(request.POST)
if form.is_valid():
obj = form.save(commit=False)
obj.updated_by = request.user
obj.save()
selected_categories = form.cleaned_data.get('category') #returns list of all selected categories e.g. ['Sports','Adventure']
#Now saving the ManyToManyField, can only work after saving the form
for title in selected_categories:
category_obj = Category.objects.get(title=title) #get object by title i.e I declared unique for title under Category model
obj.category.add(category_obj) #now add each category object to the saved form object
return redirect('confirmation', id=obj.pk)
Full Answer
models.py
class Category(models.Model):
title = models.CharField(max_length=100, null=True, unique=True)
class VideoGame(models.Model):
game_id = models.AutoField(primary_key=True)
name = models.CharField(max_length=50, blank=False, null=False)
updated_by = models.ForeignKey(settings.AUTH_USER_MODEL, blank=False, on_delete=models.CASCADE)
category = models.ManyToManyField(Category) #ManyToMany Category field
date_added = models.DateTimeField(auto_now_add=True, verbose_name="date added")
forms.py ModelForm
class VideoGameForm(forms.ModelForm):
CATEGORIES = (
('Detective', 'Detective'),
('Sports', 'Sports'),
('Action', 'Action'),
('Adventure', 'Adventure'),
)
category = forms.MultipleChoiceField(choices=CATEGORIES, widget=forms.SelectMultiple())
class Meta:
model = VideoGame
fields = ['name', 'category', 'date_added']
views.py on POST
class HostingRequestView(View):
def post(self, request, *args, **kwargs):
form = VideoGameForm(request.POST)
if form.is_valid():
obj = form.save(commit=False)
obj.updated_by = request.user
obj.save()
selected_categories = form.cleaned_data.get('category') #returns list of all selected categories e.g. ['Sports','Adventure']
#Now saving the ManyToManyField, can only work after saving the form
for title in selected_categories:
category_obj = Category.objects.get(title=title) #get object by title i.e I declared unique for title under Category model
obj.category.add(category_obj) #now add each category object to the saved form object
return redirect('confirmation', id=obj.pk)
URL path for redirect
urlpatterns = [
path('confirmation/<int:id>/', Confirmation.as_view(), name='confirmation'),
]
I hope this can be helpful. Regards
new.stores.all()
returns all stores linked to the object.
Maybe:
Change Foo to Tracks
Tracks.objects.filter(id=x) to Tracks.objects.get(id=x)
Let me know how it goes
why this confusion so much.. you are getting the id there then, call the store like
new_track.save()
new_track.store.add(request.POST['store'])

Send an email once object meets certain criteria

[Hopefully I entitled this post correctly]
I have a (sort of) 'follow' twitter thing going on. Users can follow a company profile object, which creates a follower object.
class Follower(models.Model):
created = models.DateTimeField(auto_now_add=True)
modified = models.DateTimeField(auto_now=True)
profile = models.ForeignKey(UserProfile)
company = models.ForeignKey(Company)
verified = models.BooleanField(default=False)
from_user = models.BooleanField(default=False)
...
class Company(models.Model):
owner = models.ForeignKey(User, null=True, blank=True, on_delete=models.SET_NULL)
name = models.CharField(max_length=200)
... and more fields that aren't relevant
What I'd like to do is send an update email to the company profile owner, after 5 new followers. 'You have 5 new followers!'.
As of right now I'm sending an email to the owner every time they get a new follower. A little much, I know.
I'm guessing I need to create a list of followers, send it, and then delete it to prepare for 5 new followers? I'm really not sure how to go about this. Any help or suggestions are greatly appreciated.
view:
#login_required
# this is creating a follower. Maybe I shouldn't send the email through this?
def follow(request, id):
company = Company.objects.get(id=id)
profile = get_object_or_404(UserProfile, user__username=request.user.username)
try:
follower = Follower.objects.get(profile=profile, company=company)
if not follower.verified:
follower.verified = True
follower.save()
messages.success(request, 'Now following %s\''%company.name)
mes = Message(subject="New Follower", sender=profile, recipient=company.owner)
mes.body = render_to_string('emails/email-message/new_follower.html', RequestContext(request, {
'sender': profile,
'receiver': company.owner,
}))
except ObjectDoesNotExist:
messages.error(request, 'Failed to follow.')
Send the email every time the number of followers for a specific company becomes a multiple of 5, like so:
if not (Follower.objects.filter(company=company).count() % 5):
#send the email

Categories