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'
Related
[UPDATE] The relevant models (thinned):
class Contact(TimeStampedModel, ComputedFieldsModel):
.... # Irrelevant, see reverse lookups on RegisteredStaffContact
class RegisteredStaffContact(TimeStampedModel):
contact = models.ForeignKey(Contact, models.CASCADE, related_name='registered_staffing')
staff_user = models.ForeignKey(settings.AUTH_USER_MODEL, models.CASCADE, related_name='contact_registered_staffing')
is_primary = models.BooleanField(default=False)
....
class User(TimeStampedModel, AbstractBaseUser, PermissionsMixin):
....
assistants = models.ManyToManyField('self', symmetrical=False, related_name='principals')
.... used in the method in question under variable user_principals_ids
class ShareRequest(TimeStampedModel):
contact = models.ForeignKey(to=Contact, on_delete=models.CASCADE, related_name='share_requests')
....
#classmethod
def get_inbox_for_user(cls, user: User) -> 'models.QuerySet[ShareRequest]':
user_principals_ids = list(user.principals.values_list('id', flat=True))
annotated_share_requests = cls._default_manager\
.annotate(
registered_staff_ids=ArrayAgg(
'contact__registered_staffing__staff_user',
distinct=True,
)
)\
.annotate(
primary_registered_staff_ids=ArrayAgg(
'contact__registered_staffing__staff_user',
filter=models.Q(contact__registered_staffing__is_primary=True),
distinct=True,
)
)
filtered_shared_requests = annotated_share_requests\
.filter(
(models.Q(primary_registered_staff_ids__isnull=True) & models.Q(registered_staff_ids__contains=[user.id]))
| (models.Q(primary_registered_staff_ids__isnull=True) & models.Q(registered_staff_ids__overlap=user_principals_ids))
| models.Q(primary_registered_staff_ids__contains=[user.id])
| models.Q(primary_registered_staff_ids__contained_by=user_principals_ids)
)
return filtered_shared_requests
Skipping the details, the important part is my annotate statements and further filtering by them.
From the example below, you can see that registered_staff_ids results in [None], which looks like a part of the problem:
In [39]: annotated_share_requests.first().registered_staff_ids
Out[39]: [None]
I was able to remedy it by providing an additional filter to my annotate statement, turning that expression into:
annotated_share_requests = cls._default_manager\
.annotate(
registered_staff_ids=ArrayAgg(
'contact__registered_staffing__staff_user',
filter=models.Q(contact__registered_staffing__staff_user__isnull=False),
distinct=True,
)
)\
...
Which now shows the result as:
In [40]: annotated_share_requests.first().registered_staff_ids
Out[40]: []
However, the further filtering (specifically __overlap and __contained_by lookups fail, for example:
In [56]: annotated_share_requests.values_list('primary_registered_staff_ids', flat=True)
Out[56]: <QuerySet [[], []]> # looks great
In [57]: annotated_share_requests.values_list('registered_staff_ids', flat=True)
Out[57]: <QuerySet [[], []]> # looks great
In [58]: user_principals_ids
Out[58]: [] # looks great
# BUT!
In [59]: annotated_share_requests.filter(registered_staff_ids__overlap=user_principals_ids)
Out [59]: FieldError: Cannot resolve expression type, unknown output_field
# AND SIMILARLY!
In [61]: annotated_share_requests.filter(primary_registered_staff_ids__contained_by=user_principals_ids)
Out [62]: FieldError: Cannot resolve expression type, unknown output_field
What's interesting is that it only seems to be an issue with an empty array on the right side of comparison, so this works:
In [76]: annotated_share_requests.filter(primary_registered_staff_ids__contained_by=[1])
Out[76]: <QuerySet []>
In [77]: annotated_share_requests.filter(registered_staff_ids__overlap=[1])
Out[77]: <QuerySet []>
Looking for any pointers.
In the Django documentation, they recommend writing business logic in Model.
How do the View layer or queryset access the methods in Model ?
As per example in documentation (https://docs.djangoproject.com/en/3.0/topics/db/models/)
from django.db import models
class Person(models.Model):
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
birth_date = models.DateField()
def baby_boomer_status(self):
"Returns the person's baby-boomer status."
import datetime
if self.birth_date < datetime.date(1945, 8, 1):
return "Pre-boomer"
elif self.birth_date < datetime.date(1965, 1, 1):
return "Baby boomer"
else:
return "Post-boomer"
How do view layer access the baby_boomer_status ?
I have a little experienced in Django development but I used to write logics in View itself.
This can be done by simply calling function. For example,
>>> from .models import Person
>>> person = Person.objects.get(id=1) # Remember getting the person object
>>> person.baby_boomer_status()
You have to first get person object otherwise, it will return function itself, e.g
>>> from .models import Person
>>> person.baby_boomer_status()
>>> <function AppName.models.Person.baby_boomer_status(self)>
You can just call the method on the person instance:
person = Person.objects.get(id=1)
print(person.baby_boomer_status())
You can iterate over QuerySet and call the model method as
for person in Person.objects.all():
print(person.baby_boomer_status())
If you have a single object, just call the method directly as,
print(Person.objects.get(pk=123).baby_boomer_status())
user/models.py
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
class Bread(models.Model):
title = models.CharField(max_length=40)
def __str__(self):
return self.title
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
bio = models.TextField(max_length=500, blank=True)
location = models.CharField(max_length=30, blank=True)
birth_date = models.DateField(null=True, blank=True)
picture = models.ImageField(upload_to="photos/", default="photos/none/default.png")
bread = models.ManyToManyField(Bread)
#receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs):
if created:
Profile.objects.create(user=instance)
#receiver(post_save, sender=User)
def save_user_profile(sender, instance, **kwargs):
instance.profile.save()
I trying to add a simple object to Profile model, but it does not work.
>>> from user.models import Bread
>>> from user.models import Profile
>>> from django.contrib.auth.models import User
>>> p1 = Bread(title="Example")
>>> p1.save()
>>> Profile(User).bread.add(p1)
ValueError: Cannot add "<Bread: Example>": instance is on database "None", value is on database "default"
>>> u1 = Profile(User)
>>> u1.save()
TypeError: _prepare() takes 1 positional argument but 2 were given
Seeing similar previous questions I tried to save profile model, as u can see, I know its related with User and his id, but i have no idea how to fix it easily
Did you try to fully create a Profile object and save it before adding Bread instances to it?
Also i notice you're passing a model class to a model constructor. You should pass an instance of a user.
Also your profile model requires some other fields.
I would be rather explicit than implicit in creating it as in:
u1 = Profile.objects.create(
user=myuser,
bio="blablabla",
location="blublublue",
)
then and only then, i would add the bread instance to the Profile instance
hope it's clear.
>>> from api.models import Film
>>> from user.models import Profile
>>> from django.contrib.auth.models import User
>>> from django.shortcuts import render, redirect, get_object_or_404
>>> user = get_object_or_404(Profile)
>>> user
<Profile: Profile object>
>>> lobster = get_object_or_404(Film, id=22)
>>> lobster
<Film: Lobster>
>>> a = user.film.add(lobster)
>>> another_movie = get_object_or_404(Film, id=23)
>>> another_movie
<Film: What I actually do>
>>> user.film.add(another_movie)
>>> film = bloader.film.all()
>>> film
<QuerySet [<Film: Lobster>, <Film: What I actually do>]>
>>>
I tried like this and Its working, thank you #efkin.
Suppose the following models:
class DeltaCheck(models.Model):
logs = generic.GenericRelation('Log')
title = models.CharField(max_length=50)
owner = models.ForeignKey(User)
class Log(models.Model):
title = models.CharField(max_length=50)
content_type = models.ForeignKey(ContentType)
object_id = models.PositiveIntegerField()
content_object = generic.GenericForeignKey('content_type', 'object_id')
If I create a DeltaCheck and a couple of Logs and then delete the DeltaCheck, the Logs are deleted as well:
In [7]: Log.objects.count()
Out[7]: 10
In [8]: DeltaCheck.objects.get().delete()
In [9]: Log.objects.count()
Out[9]: 0
BUT if I delete the User (the field owner), the DeltaCheck gets deleted BUT not the Logs, look:
In [14]: Log.objects.count()
Out[14]: 10
In [15]: DeltaCheck.objects.get().owner.delete()
In [16]: DeltaCheck.objects.all()
Out[16]: []
In [17]: Log.objects.count()
Out[17]: 10
Why is that? Seems like a bug.
EDIT 1:
Curiously, both pre_delete and post_delete signals are fired in the failing case... What happens in the middle?
EDIT 2:
Ok this is weird as heck. If I define an EMPTY receiver for the pre_delete signal... it works :/ Just added:
#receiver(pre_delete)
def recv(**kwargs):
pass
And now it works...
As the bug report states, it was certainly a bug that has now been fixed.
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.