Django ManyToManyField Retrieving both objects error - django

I implemented a ManyToManyField for a following feature which allows users to follow other users and if they follow other user .
They would able to retrieve their's objects.
This is my module
class Person(models.Model):
user = models.ForeignKey(User)
name = models.CharField(max_length=100, blank=True)
image = models.FileField(upload_to="images/",blank=True)
following = models.ManyToManyField('self', related_name='followers', symmetrical=False, blank=True, null=True)
birthday = models.DateField(blank=True,null=True)
def __unicode__(self):
return self.name
class Board(models.Model):
user = models.ForeignKey(User)
name = models.CharField(max_length=100)
created = models.DateTimeField(auto_now_add=True)
picture = models.OneToOneField('Picture',related_name='picture',blank=True,null=True)
def __unicode__(self):
return self.name
The problem is when a user is following 2 users . I am only able to retrieve a single user objects instead of both users.
For example I created 3 users , Jimmy , Sarah and Simon . Sarah is following Simon and Jimmy
Jimmy has a board called Jimmy's Math Board ,Jimmy English Board
Simon has a single board called Simon's Whiteboard
The problem is underneath
>>> from pet.models import *
>>> from django.contrib.auth.models import User
>>> user = User.objects.get(username='Sarah')
>>> person = Person.objects.get(user=user)
>>> sarah = Person.objects.get(user=user)
>>> sarah.following.all() # SARAH IS FOLLOWING JIMMY AND SIMON
[<Person: Jimmy>, <Person: Simon>]
>>> users = User.objects.filter(pk__in=sarah.following.all().values_list('user__pk',flat=True))
>>> users
[<User: Jimmy>, <User: Simon>] # I'm able to retrieve their users
>>> board = Board.objects.filter(user=users) # when I search for their boards , I only retrieve a single user . Jimmy's board not Simon
>>> board
[<Board: Jimmy's Math Board>, <Board: Jimmy English Board>]
>>> person = Person.objects.filter(user=users)
>>> person
[<Person: Jimmy>]
How Can I retrieve both of these users board?

Because board = Board.objects.filter(user=users) is filtering by user it expects one user to be provided. If you were to do something like board = Board.objects.filter(user__in=users) which uses the __in filter, the filtering will correctly use the list of user objects.
You could also use a flat list of id's instead of objects like so board = Board.objects.filter(user__in=sarah.following.all().values_list('user__pk',flat=T‌​rue))

Related

Filter a contenttype with list of content object's field

I creating an app where users can post with its related tags:
class Tag(models.Model):
name = models.CharField(max_length=255, unique=True)
class Post(models.Model):
user = models.ForeignKey(User)
body = models.TextField()
tags = models.ManyToManyField(Tag)
pub_date = models.DateTimeField(default=timezone.now)
activity = GenericRelation(Activity, related_query_name="posts")
class Photo(models.Model):
user = models.ForeignKey(User)
file = models.ImageField()
tags = models.ManyToManyField(Tag)
pub_date = models.DateTimeField(default=timezone.now)
activity = GenericRelation(Activity, related_query_name="photos")
class Activity(models.Model):
actor = models.ForeignKey(User)
verb = models.PositiveIntegerField(choices=VERB_TYPE)
content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
object_id = models.PositiveIntegerField()
content_object = GenericForeignKey('content_type', 'object_id')
pub_date = models.DateTimeField(default=timezone.now)
What I want to do is get/filter maximum of 5 latest/recent Activity objects, with the a list of users, and a list of tags from the list of Post objects tags field and return json using django-rest-framework to view in the client side.
Eg activities:
UserA created a new Post obect with tags(#class, #school)
UserB created a new Post object with tags(#professor, #teacher)
UserC created a new Post object with tags(#school, #university)
UserD created a new Post object with tags(#university, #school)
So say I want to filter Activity with user_list=[UserA, UserC] and tag_list = [#class, #teacher]
It should return:
UserA created a new Post obect with tags(#class, #school)
UserC created a new Post object with tags(#school, #university)
UserB created a new Post object with tags(#professor, #teacher)
To filter the Activity with users, I can query this way:
Activity.objects.filter(actor__in=user_list)
But, how do I filter Activity with the content_object's (i.e.Post or Photo) field (i.e. Post.tags or Photo.tags)? Now I am doing this way:
Activity.objects.filter(posts__tags__in=tag_l)
Activity.objects.filter(photos__tags__in=tags)
So to sum up, If I have need activities with list of users and list of tags I have to do like this:
activites = Activity.objects.filter(
Q(actor__in=user_list) |
Qposts__tags__in=tag_list) |
Q(photos__tags__in=tag_list)
)
But suppose there will be more than two ContentType model classes then I'd have to again add another Q(moreModel__tags__in=tag_list). So, I hope there's a better way to optimize the process.
I'd say your best bet would be to use a filter from Django filter (link to the rest framework docs), specifically a ModelMultipleChoiceFilter. I'm going to assume you already have an ActivityViewSet to go along with the Activity model.
Firstly you'll want to create a django_filters.FilterSet, probably in a new file such as filters.py, and set up the ModelMultipleChoiceFilter, like so:
import django_filters
from .models import Activity, Tag, User
class ActivityFilterSet(django_filters.FilterSet):
tags = django_filters.ModelMultipleChoiceFilter(
name='content_object__tags__name',
to_field_name='name',
lookup_type='in',
queryset=Tag.objects.all()
)
users = django_filters.ModelMultipleChoiceFilter(
name='content_object__user__pk',
to_field_name='pk',
lookup_type='in',
queryset=User.objects.all()
)
class Meta:
model = Activity
fields = (
'tags',
'users',
)
Then you'll want to tell your viewset to use that filterset:
from .filters import ActivityFilterSet
# ...
class ActivityViewSet(GenericViewSet):
# all your existing declarations, eg.,
# serializer_class = ActivitySerializer
# ...
filter_class = ActivityFilterSet
# ...
Once you've got that done, you'll be able to filter your results using GET parameters, eg.,
GET /activities?users=1 – everything created by User 1
GET /activities?users=1&users=2 – everything created by User 1 or User 2
GET /activities?users=1&tags=class – everything created by User 1 with the tag #Class
GET /activities?users=1&users=2&tags=class&tags=school – everything created by User 1 or User 2 with the tags #Class or #School
and so on
I will give you an example from django updown, it have a similiar model: https://github.com/weluse/django-updown/blob/master/updown/models.py#L31
>>> from updown.models import Vote
>>> Vote.objects.first()
<Vote: john voted 1 on Conveniently develop impactful e-commerce>
>>> v = Vote.objects.first()
>>> v.content_object
<Thread: Conveniently develop impactful e-commerce>
>>> v.content_object.__class__
<class 'app_forum.models.Thread'>
>>> [ v.content_type for v in Vote.objects.all() if v.content_object.__class__.__name__ == 'Thread' ]
[<Thread: Conveniently develop impactful e-commerce>, <Thread: Quickly evisculate exceptional paradigms>]
>>>
# You can also use with
>>> user_ids = [ u.content_type.id for u in Vote.objects.all() if u.content_object.__class__.__name__ == 'User' ]
>>> user_ids
[1, 52, 3, 4]
>>> from django.contrib.auth.models import User
>>> User.objects.filter(pk__in=user_ids)
[<User: John>, <User: Alex>, <User: Roboto>, <User: Membra>]
>>>
>>> from django.contrib.contenttypes.models import ContentType
>>> ContentType.objects.get_for_model(v.content_object)
<ContentType: Detail Thread>
>>>
You also can use ContentType.objects.get_for_model(model_instance) such as this: https://github.com/weluse/django-updown/blob/master/updown/fields.py#L70
In your problem, maybe can use with this..
>>> photo_ids = [ ac.content_type.id for ac in Activity.objects.all() if ac.content_object.__class__.__name__ == 'Photo' ]
>>> Activity.objects.filter(content_type__id__in=photo_ids)
# or
>>> photo_ids = [ ac.content_type.id for ac in Activity.objects.all() if content_type.model_class().__name__ == 'Photo']
>>> Activity.objects.filter(content_type__id__in=photo_ids)
Hope it can help..
For this method set the related_query_name to the name of the model in lower case or verbose_name of the model.
You can first filter out the content type present in the Activity model.
content_types = ContentType.objects.filter(activity__id__isnull=False)
Now use these content types to build the lookup.
q = Q(actor__in=user_list)
for content_type in content_types:
arg = content_type.name + '__tags__in'
kwargs = {arg: tag_list}
q = q | Q(**kwargs)
Now you can filter the activities using this lookup.
activities = Activity.objects.filter(q).distinct()

Django QuerySet Error

I have a simple module below and I have a field called following in the Person module.
class Person(models.Model):
user = models.ForeignKey(User)
name = models.CharField(max_length=100)
image = models.FileField(upload_to="images/",blank=True,null=True)
following = models.ManyToManyField('self', related_name='followers', symmetrical=False, blank=True, null=True)
def __unicode__(self):
return self.name
I created 3 accounts called sarah , jim and ben. I managed to get Sarah to follow jim and ben but when I display who sarah followed , it return a querysets not their object themself . How can I work in reverse to get them into objects themself so I can relate them to my Person module and display the names of the people who sarah are following.
This is the part I don't understand
>>> from pet.models import *
>>> from django.contrib.auth.models import User
>>> sarah = User.objects.get(username='Sarah')
>>> jim = User.objects.get(username='Jim')
>>> ben = User.objects.get(username='Ben')
>>> Sarah = Person.objects.get(user=sarah)
>>> Jim = Person.objects.get(user=jim)
>>> Ben = Person.objects.get(user=ben)
>>> Sarah.following.add(Jim,Ben) //setup the many to many relationship
>>> Sarah.save()
>>> Sarah.following.all() // return a queryset of Person objects which john is following.
[<Person: Jim>, <Person: Ben>]
I want to return all the user objects that user are following in my template.
#SI Eric
>>> Sarah.following.all().values_list('name',flat=True)
[u'Jim', u'Ben']
>>> p = Sarah.following.all().values_list('name',flat=True)
>>> person = Person.objects.filter(user=p)
>>> print person
class Board(models.Model):
user = models.ForeignKey(User)
name = models.CharField(max_length=100)
def __unicode__(self):
return self.name
Sarah.following.all().values_list('name', flat=True)
would output
['Jim', 'Ben']
As bernie suggested, you can set the unicode function and the default queryset returned will do the same. In this particular type of instance I prefer to be more explicit with what I'm getting from the objects, but that's just me.
Edit after question was updated:
I'm not sure I follow what you're trying to do. You have a queryset of the objects of the people who are following sarah. If you just want the names, the values list above will work. If you want to iterate over each individual object then you would do something like this:
for person in Sarah.following.all():
# person is an instance of Person
# you can access all the properties of the object by accessing person
print person.name
Alternatively, pass the entire queryset to the template by adding it to the context in the view:
context['following'] = Sarah.following.all()
Then in the template you could do this (for example):
<ul>
{% for person in following %}
<li>{{ person.name }}</li>
{% endfor %}
</ul>
Edit:
Both from within the template and the view code you can access any of the properties of each 'Person' object by just directly referencing them. So for example if you want to access the properties of the 'User' object that person has a Foreign Key relation to, you would just access it's properties directly, like so:
person.user.username
person.user.email
etc...
One last edit:
To get a queryset of 'User' objects based on the 'following` queryset for a person, you could do this:
users = User.objects.filter(pk__in=Sarah.following.all().values_list('user__pk', flat=True)
From there you should be able use that filter boards and other objects that reference the User model:
boards = Board.objects.filter(user__in=users)

Django Query Does not exist

I'm been trying to create an app that allows users to follow each other profile since yesterday and today and I haven't been successful so far.
I'm having trouble creating a following function that allows me to retrieve users from a particular user he follows.
Example . If John follows Diana . I want to able to retrieve the user called Diana and use it with my modules.
I'm really sorry if this doesn't make sense . I'm trying my hardest to explain my situation.
class Person(models.Model):
user = models.ForeignKey(User)
name = models.CharField(max_length=100)
image = models.FileField(upload_to="images/",blank=True,null=True)
class Board(models.Model):
user = models.ForeignKey(User)
name = models.CharField(max_length=100)
def __unicode__(self):
return self.name
Most of these solutions gave me no query
This was one of the solutions I tried.
class UserLink(models.Model):
from_user = models.ForeignKey(User , related_name = "following_set")
to_user = models.ForeignKey(User , related_name = "follower_set")
date_added = models.DateTimeField(default = datetime.now)
def __unicode__(self):
return "%s is following %s" % (self.from_user.username,self.to_user.username)
def save(self,**kwargs):
if self.from_user == self.to_user:
raise ValueError("Cannot follow yourself ")
super(UserLink , self).save(**kwargs)
class Meta:
unique_together = (('to_user','from_user'),)
I tried to retrieve the users that a particular user followed and use it against my modules such as Person but it gave me an error No query exist.
def Follow(request,username=""):
if request.method == "POST":
username = request.POST.get('follow',False)
user = User.objects.get(username=username)
UserLink.objects.create(from_user=request.user,to_user=user)
return HttpResponseRedirect(reverse('world:Profile'))
return HttpResponseRedirect(reverse('world:Profile'))
I also tried this following function but it only followed himself and I changed self to User but it didn't allow me to put the person to follow
class UserProfile(models.Model):
user = models.OneToOneField(User)
follows = models.ManyToManyField('self', related_name='followed_by', symmetrical=False)
>>>from pet.models import *
>>>from django.contrib.auth.models import User
>>>user = User.objects.get(username='Peter')
>>>user1 = User.objects.get(username='Sarah')
>>>p = UserProfile.objects.filter(user=user,follows=user1)
>>>Error no field called follows
How can I create a following class that allows retrieve the people that they followed and use it with my modules such as Person?
Can someone help me . Thannk you community!
If I understand correctly, youu are on the right track with the many to many relationship. What you need is to modify your existing Person class to include this information.
Since information about who someone follows or is following is essentially information about that person and so you shouldn't really need to define a new class to implement that functionality.
I would suggest modifying your Person like so.
class Person(models.Model):
user = models.ForeignKey(User)
name = models.CharField(max_length=100)
image = models.FileField(upload_to="images/",blank=True,null=True)
following = models.ManyToManyField('self', related_name='followers', symmetrical=False, blank=True, null=True)
What this line does is makes a many to many relationship between the class Person and its self.
Many to many relationships work a little different to other relationships and I suggest you read the Django documentation https://docs.djangoproject.com/en/dev/topics/db/examples/many_to_many/.
But you should now be able to setup and access the relationship like this.
>>>john = Person.objects.get(name="John")
>>>diana = Person.objects.get(name="Diana")
>>>john.following.add(diana)//setup the many to many relationship
>>>john.save()
>>>john.following.all()
//This should return a queryset of Person objects which john is following.
//eg Diana
>>>diana.followers.all()
//This should return a queryset of Person objects which are following Diana.
//eg. John.
Easy, how awesome is Django!

Querying a Django model with ForeignKey and ManyToMany

I'm trying to teach myself Django and built a simple model for a teacher listing app. There are course levels (primary, secondary, high school, etc), courses, teachers and teacher-course memberships.
I want to fetch course levels & courses whose teachers are subscribed to a specific membership. Currently I'm doing following (obviously a very bad practice, but as DB is very small it's working properly):
course_levels = CourseLevel.objects.prefetch_related('course_set')
# get mainpage teachers
# TODO: This is inefficient, there must be some cool way of doing the same.
for course_level in course_levels:
for course in course_level.course_set.
course.visible_teachers = course.teachers.filter(membership__type=Membership.TYPE_FRONT_PAGE)
And here is models.py
class Teacher(models.Model):
name = models.CharField(max_length=64)
class CourseLevel(models.Model):
name = models.CharField(max_length=64)
class Course(models.Model):
name = models.CharField(max_length=64)
# courses can have levels
level = models.ForeignKey(CourseLevel)
teachers = models.ManyToManyField(Teacher, through='Membership')
class Membership(models.Model):
(TYPE_FRONT_PAGE, TYPE_COURSE_PAGE, TYPE_BASIC) = (1,2,3)
teacher = models.ForeignKey(Teacher, related_name='membership')
course = models.ForeignKey(Course)
MEMBERSHIP_TYPES = (
(TYPE_FRONT_PAGE, _('Front Page')), # Display users on the front page
(TYPE_COURSE_PAGE, _('Course Page')), # Display users on the front of course page
(TYPE_BASIC, _('Basic Membership')), # Display users after
)
type = models.IntegerField(max_length=2, choices=MEMBERSHIP_TYPES)
What's the better way to fetch these records, instead of iterating over courses and fetching related teachers in Django Query API?
Thanks in advance.
I am not sure it this exactly answers what you want but, is this the type of search you want to do?
Membership.objects.filter(type = 1, course__level__name = 'college')
I made two teachers, both who are type 1, both teach the class 'fun', and one who teaches 'fun' in 'college' and another who teaches 'fun' in 'grade'. Here are these outputs of a couple of searches:
>>> members = Membership.objects.filter(type=1)
>>> members
[<Membership: Membership object>, <Membership: Membership object>]
>>> members.filter(course__level__name='college')
[<Membership: Membership object>]
>>> Membership.objects.filter(type=1, course__level__name='college')
[<Membership: Membership object>]
Maybe this search is most like the one you were asking for:
>>> Membership.objects.filter(course__name='fun', type=1)
[<Membership: Membership object>, <Membership: Membership object>]

Django many-to-many relationship with extra fields

I am building a simple interface to a biological database using the django-admin to populate the db. I want tot to use a many-to-many relationship for a questionnaire to fish species (one questionnaire can have more than one species and one species can be present in more than one questionnaire). The two models in question:
class Species(models.Model):
fish_spp_name = models.CharField(max_length=255, unique=True)
class Questionaire(models.Model):
# ...
fish_caught = models.ManyToManyField(Species)
now, I want to my data to contain a number of each species caught, per questionnaire. So, for example, I can associate 3 different species with questionnaire id=1, but how do I include that, say 2 of the first species, 1 of the second and 4 of the third were caught?
Check this: Extra fields on many-to-many relationships
Define another models Caught to hold the information per catch. Give it a related_name to make it easier to refer to in your code. You might also want to unique_together appropriate fields.
class Species(models.Model):
name = models.CharField(max_length=255, unique=True)
def __unicode__(self):
return '%s/%d' % self.name
class Questionaire(models.Model):
pass
class Caught(models.Model):
species = models.ForeignKey(Species)
number = models.IntegerField()
questionaire = models.ForeignKey(
Questionaire, related_name='catches')
def __unicode__(self):
return '%s/%d' % (self.species.name, self.number)
Use it like this:
(InteractiveConsole)
>>> from app.models import *
>>> s1 = Species(name='Salmon')
>>> s1.save()
>>> s2 = Species(name='Mackerel')
>>> s2.save()
>>> q = Questionaire()
>>> q.save()
>>> c1 = Caught(species=s1, number=7, questionaire=q)
>>> c2 = Caught(species=s2, number=5, questionaire=q)
>>> c1.save()
>>> c2.save()
>>> q.catches.all()
[<Caught: Salmon/7>, <Caught: Mackerel/5>]
>>>