Following users like twitter in Django, how would you do it? - python

I am playing with relationships in Django/python and I am wondering how you guys would create a relationship between a User and his followers and a Follower to the users he follows.
Would love to read your opinion...

First, you should understand how to store additional information about users. It requires another model that has a relation to one user, the "profile" model.
Then, you could use an M2M field, assuming you'd use django-annoying, you could define your user profile model as such:
from django.db import models
from annoying.fields import AutoOneToOneField
class UserProfile(models.Model):
user = AutoOneToOneField('auth.user')
follows = models.ManyToManyField('UserProfile', related_name='followed_by')
def __unicode__(self):
return self.user.username
And use it as such:
In [1]: tim, c = User.objects.get_or_create(username='tim')
In [2]: chris, c = User.objects.get_or_create(username='chris')
In [3]: tim.userprofile.follows.add(chris.userprofile) # chris follows tim
In [4]: tim.userprofile.follows.all() # list of userprofiles of users that tim follows
Out[4]: [<UserProfile: chris>]
In [5]: chris.userprofile.followed_by.all() # list of userprofiles of users that follow chris
Out[5]: [<UserProfile: tim>]
Also, note that you could check / reuse apps like django-subscription, django-actstream, django-social (harder to use probably)...
You might want to take a look at the django packages for notifications and activities as they all require some follow/subscription database design.

This is how I would do it:
class Tweeter(models.Model):
user = models.ManyToManyField('self', symmetrical=False, through='Relationship')
class Relationship(models.Model):
who = models.ForeignKey(Tweeter, related_name="who")
whom = models.ForeignKey(Tweeter, related_name="whom")
In the shell,
In [1]: t = Tweeter()
In [2]: t.save()
In [3]: f = Tweeter()
In [4]: f.save()
In [5]: r=Relationship()
In [6]: r.who=t
In [7]: r.whom=f
In [8]: r.save()
In [18]: Relationship.objects.all()[0].who.id
Out[18]: 1L
In [19]: Relationship.objects.all()[0].whom.id
Out[19]: 2L

Edit: Makes more sense to use ManyToManyField, as the commenter suggests. Users can have 0-x User followers, and Users can follow 0-x Users.
https://docs.djangoproject.com/en/1.3/ref/models/fields/#manytomanyfield
Without going into code, not much more to be said.

Related

How to change value of choice field in Django shell?

I have started Django in about a month ago and I am having problem in it.
I had created a model which is one to one linked with User model
This is my models.py file:
from django.db import models
from django.contrib.auth.models import User
from django.db.models.signals import post_save
from django.dispatch import receiver
# Create your models here.
class UserInfo(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
options = (('individual','Individual'),
('institute',"An institute"),
)
userAs = models.CharField(max_length=100,choices=options)
def __str__(self):
return self.user.username
class Meta:
ordering = ["user"]
#receiver(post_save,sender=User)
def create_user_profile(sender, instance, created, **kwargs):
if created:
UserInfo.objects.create(user=instance)
...and I want to change value of userAs from shell and I am unable to do it.
This is what I tried:
In [10]: us = User.objects.get(username="foo")
In [11]: us.userinfo.userAs
Out[11]: 'individual'
In [12]: type(us.userinfo.userAs)
Out[12]: str
In [13]: us.userinfo.userAs = 'institute'
In [14]: us.save()
In [15]: us.userinfo.userAs
Out[15]: 'institute'
In [16]: us = User.objects.get(username="steve04")
In [17]: us.userinfo.userAs
Out[17]: 'individual'
...but I am unable to change it.
Your problem
You're modifying us.userinfo and saving your us instance. However, us is unchanged. You modified your userinfo object that is inside us, but not us itself.
Solution
You should save us.userinfo instead of us, here is a demo:
In [1]: us = User.objects.get(username="foo")
In [2]: us.userinfo.userAs = 'institute'
In [3]: us.userinfo.save()
In [4]: us = User.objects.get(username="steve04")
In [5]: us.userinfo.userAs
Out[5]: 'institute'

Determine if an M2M field has a custom through model

With a custom through model for an M2M relationship, .add(), .create() and .remove() are disabled.
At the moment, I attempt to use .add() (or whatever) and catch and deal with AttributeError for those custom M2M relationships.
Is there an 'official' way to identify a custom through model, using the Meta API or otherwise? At this stage in my processing, I would rather treat all custom through relationships as generically as possible (rather than lots of if m2m_field.related.through == FooBar statements)
(A solution was found for Django 1.8, but I'm starting a bounty for 2.2)
Looks as though
m2m_field.related.through._meta.auto_created is False
does the job.
From Django 2.2 the .add(), .create(), etc. methods are able to work with a custom through Model as long as you provide the corresponding values for the required fields of the intermediate model using through_defaults:
From the documentation:
You can also use add(), create(), or set() to create relationships, as long as you specify through_defaults for any required fields:
>>> beatles.members.add(john, through_defaults={'date_joined': date(1960, 8, 1)})
>>> beatles.members.create(name="George Harrison", through_defaults={'date_joined': date(1960, 8, 1)})
>>> beatles.members.set([john, paul, ringo, george], through_defaults={'date_joined': date(1960, 8, 1)})
You may prefer to create instances of the intermediate model directly.
The .remove() method's behavior needs a bit of attention:
If the custom through table defined by the intermediate model does not enforce uniqueness on the (model1, model2) pair, allowing multiple values, the remove() call will remove all intermediate model instances:
>>> Membership.objects.create(person=ringo, group=beatles,
... date_joined=date(1968, 9, 4),
... invite_reason="You've been gone for a month and we miss you.")
>>> beatles.members.all()
<QuerySet [<Person: Ringo Starr>, <Person: Paul McCartney>, <Person: Ringo Starr>]>
>>> # This deletes both of the intermediate model instances for Ringo Starr
>>> beatles.members.remove(ringo)
>>> beatles.members.all()
<QuerySet [<Person: Paul McCartney>]>
About the _meta field I haven't been able to access it from the m2m field, but the above part of the documentation seems to allow avoiding the "gymnastics" of accessing _meta.
If I find anything interesting I will update my answer accordingly.
For django 2.2 you can directly check the whether the through model is autocreated or not
This can be checked through a direct check like the following
# check for the auto_created value
m2m_field.through._meta.auto_created == False
To test this I created some sample clases with multiple M2M fields one with a custom through field and one with default class.
from django.db import models
import uuid
class Leaf(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
name = models.CharField(null=True, max_length=25, blank=True)
# Create your models here.
class MainTree(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
new_leaves = models.ManyToManyField(Leaf, through='LeafTree')
leaves = models.ManyToManyField(Leaf, related_name='main_branch')
class LeafTree(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
tree = models.ForeignKey(MainTree, on_delete=models.CASCADE)
leaf = models.ForeignKey(Leaf, on_delete=models.CASCADE)
Testing our method
In [2]: from trees.models import MainTree
In [3]: m = MainTree()
In [4]: m.leaves.through._meta
Out[4]: <Options for MainTree_leaves>
In [5]: m.leaves.through._meta.auto_created
Out[5]: trees.models.MainTree
In [6]: m.new_leaves.through._meta.auto_created
Out[6]: False
In [7]: m.new_leaves.through._meta.auto_created == False
Out[7]: True
In [8]: m.leaves.through._meta.auto_created == False
Out[8]: False

Django REST Framework serializer with different models

I have three different models that I want to gather in a feed type page. They do all contain different types of things but for simplicity, the models are the same in this instance.
class ObjectA(models.Model):
text = models.TextField()
pub_date = models.DateTimeField('date published',auto_now_add=True)
...
class ObjectB(models.Model):
text = models.TextField()
pub_date = models.DateTimeField('date published',auto_now_add=True)
...
class ObjectC(models.Model):
text = models.TextField()
pub_date = models.DateTimeField('date published',auto_now_add=True)
...
What would be the general idea to serialize lists of all three objects into one list ordered by pub_date using the Django REST Framework. I just have experience using the meta version below but it can only deal with one model I am assuming. Thanks in advance.
class ObjectAListSerializer(serializers.ModelSerializer):
class Meta:
model = ObjectA
fields = [
'text',
'pub_date'
]
Pretty much trying to create something that would work like this:
class AllObjectsListSerializer(serializers.ModelSerializer):
The most important thing here is that you shouldn't be having three different models here. If they are storing the same data, there should be only one model. To have three models means every time you need to execute a statement you need to precede that with a IF ELIF ELSE which is far from DRY. Not to mention the fact that you need to do a UNION as suggested by Wtower in his answer
Merge the models.
You need to make a union on the querysets:
query_union = query1 | query 2
And you need a custom serializer for the fields of that union. Or if the union fields are all the same from any of the models, possibly use those model's modelserializer, but haven't tested that one.
dataA = ASerializer(queryset_A, many=True).data
dataB = BSerializer(queryset_B, many=True).data
dataC = CSerializer(queryset_C, many=True).data
just
return Response(data={'dataA ': dataA , 'dataB ': dataB ,'dataC ': dataC })
If you want return a list,the item is {'a':a,''b':'b,'c':c},you should declare you relation between a,b,c and can filter b and c with a.If so,i will write an example for you.

Querying a django model using a model name string input

Let's say i have the following django models:
class ModelB(models.Model):
title = models.CharField(max_length=20)
class ModelD(models.Model):
name = models.CharField(max_length=20)
In the django ORM, i am trying to read a string which will be the name of a model, and use it to query. Something like this:
>>b = 'ModelB'
>>b.objects.all()
This will obviously not work as it is a string. I have looked at ContentType but i am not sure how it will be helpful in my scenario. Any suggestions?
I also tried doing a get operation on Contentype like this:
>>> z = ContentType.objects.get(model='modelb')
>>> z
<ContentType: model b>
>>> z.__dict__
{'model': u'modelb', '_state': <django.db.models.base.ModelState object at 0x7f195346c150>, 'id': 14, 'app_label': u'testapp'}
>>>
But i did not know how to proceed further from here!
If you're using Django < 1.7, I think you can do this.
from django.db.models.loading import get_model
z = ContentType.objects.get(model='modelb')
ModelB = get_model(z.app_label, 'ModelB')
For django >= 1.7, you can
from django.apps import apps
z = ContentType.objects.get(model='modelb')
ModelB = apps.get_model(z.app_label, 'ModelB')
you can then use ModelB to query.

Updating Django model using selected collection of objects

I am learning Django and I refer djangobook.com.
I have a model like this:
class Publisher(models.Model):
name = models.CharField(max_length=30)
address = models.CharField(max_length=50)
city = models.CharField(max_length=60)
state_province = models.CharField(max_length=30)
country = models.CharField(max_length=50)
website = models.URLField()
def __unicode__(self):
return self.name
I have populated some data (as described in the demo) and tried to update the value of a particular field of a record (as explained) and this works perfectly fine:
>>> publisher_list = Publisher.objects.all()
>>> p = publisher_list[0]
>>> p.name = u'Apress'
>>> p.save()
But when I try the below (which I assume is equivalent to the above), it does not work. The name is not updated in the database. What am I doing wrong here?
>>> publisher_list = Publisher.objects.all()
>>> publisher_list[0].name = 'Apress'
>>> publisher_list[0].save()
Reference: http://djangobook.com/en/2.0/chapter05/
Thanks.
Your indexing is retrieving the model from the database multiple times. Bind the model to a name before mutating and saving it.
According to QuerySet.__getitem__(), retrieving by item, for example by qs[0], is not cached and will hit DB for each accessing, unless the queryset has been evaluated and thus cache has been filled:
>>> qs = User.objects.all()[:10]
>>> qs[0] is qs[0] # Similar with your issue, you modified the attribute of the first and tried to save the latter.
False
>>> len(qs) # evaluate and fill cache
10
>>> qs[0] is qs[0]
True
So you could
follow Ignacio's answer, or just p = Publisher.objects.all()[0]
or, evaluate the queryset before indexing. Whether this could be easier depends on your code logic.

Categories