django model - fetching user data accross multiple tables - django

I am writing a django (1.10) website and using allauth for authorisations. I don't want to extend the user model in django - because allauth adds a further layer of complexity to what is already a seemingly convoluted process.
I want to create a model (Custom UserManager?) that will have the following methods:
get_all_subscriptions_for_user(user=specified_user)
get_unexpired_subscriptions_for_user(user=specified_user)
Note: unexpired subscriptions are defined by subscriptions whose end_date > today's date.
This is a snippet of my models.py below
from django.db import models
from django.contrib.auth.models import User
#...
class Subscription(models.Model):
token = models.CharKey()
start_date = models.DateTime()
end_date = models.DateTime()
# other attributes
class UserSubscription(models.Model):
user = models.ForeignKey(User)
subscription = models.ForeignKey(Subscription)
# In view
def foo(request):
user = User.objects.get(username=request.user)
# how can I implement the following methods:
# get_all_subscriptions_for_user(user=specified_user)
# get_unexpired_subscriptions_for_user(user=specified_user)
Ideally, I would like to have a custom user manager, which can fetch this data in one trip to the database - but I'm not sure if I can have a custom user manager without having a custom user model.
[[Aside]]
I'm trying to avoid using a custom model, because it wreaks havoc on the other applications (in my project) which have User as a FK. makemigrations and migrate always barf with a message about inconsistent migration history

You can go with a custom Manager, don't need a UserManager since you are fetching related models:
class UserSubscriptionManager(models.Manager):
def for_user(self, user):
return super(UserSubscriptionManager, self).get_queryset().filter(user=user)
def unexpired_for(self, user):
return self.for_user(user).filter(
suscription__end_date__gt=datetime.date.today() # import datetime
)
in your models:
class UserSubscription(models.Model):
user = models.ForeignKey(User)
subscription = models.ForeignKey(Subscription)
user_objects = UserSubscriptionManager()
this way you can do chain filters in the view, for example:
unexpired_suscriptions = UserSubscription.user_objects().unexpired_for(
user=request.user
).exclude(suscription__token='invalid token')

Try this:
response = []
user_sub = UserSubscription.objects.filter(user=user.pk)
for row in user_sub:
subscription = Subscription.objects.get(pk=row.subscription)
end_date = subscription.end_date
if end_date > timezone.now():
response.append(subscription)

Related

Edit update all schedules except primary key in Django Rest Framework

I wanna change all fields of a json object except 'pk' in DRF. I just need to keep one json data. When adding a new data ,this one should override existing data. Is there a way to do it with django ?
my models.py
class ClientUser2(models.Model):
phone_number = models.CharField(max_length=20,unique=True)
name = models.CharField(max_length=100,blank=True)
status = models.IntegerField(default=1)
class ClientNameSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = ClientUser2
fields = ('url','phone_number','name','status','pk')
my views.py
class ClientViewSet(viewsets.ModelViewSet):
"""
API endpoint that allows messages to be viewed or edited.
"""
queryset = ClientUser2.objects.all()
serializer_class = ClientNameSerializer
and it's my api root
api_root
If you want to be able to only retrieve and update models you can use RetrieveUpdateApiView
Reference : https://www.django-rest-framework.org/api-guide/generic-views/#retrieveupdateapiview

Django - Update field values in extended user model after a request is performed

My project is a social networking site that can send requests to friends and make friends.
I have extended django's existing user model using oneToone field .
So far i've been able to do the above said thing but when ever a user accepts the request , Both the user who sent request and also the user accepted it must increment a value in their extended user model which stores the value of their friends count .
I'm facing difficulties trying to solve this .
I've also used signals but it doesn't work .
Here is my code:
models.py:
class Friends(models.Model):
"""Model for saving relationship of user and friends"""
request_id = models.ForeignKey(User,on_delete=models.CASCADE,related_name='current_user')
friend_id = models.ForeignKey(User,on_delete=models.CASCADE,related_name='user_friend')
created_on = models.DateTimeField(auto_now_add=True,auto_now=False)
class Meta:
verbose_name_plural = "Friends"
def __str__(self):
return str(self.friend_id)
class Profile(models.Model):
user = models.OneToOneField(User,related_name='profile',on_delete=models.CASCADE)
location = models.CharField(max_length=30,blank=True)
friends_count = models.PositiveIntegerField(default=0)
profile_pic = models.ImageField(upload_to='profile_pics/',blank=True,null=True)
def natural_key(self):
return (self.user.username,)
signals.py:
#receiver(post_save,sender=Friends)
def update_friends_count(sender,instance,created,**kwargs):
if created:
user_profile = Profile(user = instance.request_id)
user_profile.friends_count=F('friends_count')+1
user_profile.save()
Any help would be greatly appreciated!!!!! Thank you in advance!!!
You were initializing a new instance of Profile every time a friend is made. Instead you can use get_or_create to generate a profile or retrieve the one associated with that id. Next, you want to update both users so fetch the other update the counts and save.
#receiver(post_save,sender=Friends)
def update_friends_count(sender,instance,created,**kwargs):
if created:
user_profile, profile_created = Profile.objects.get_or_create(user = instance.request_id)
user_profile.friends_count=F('friends_count')+1
user_profile.save()
friend_profile, profile_created = Profile.objects.get_or_create(user = instance.friend_id)
friend_profile.friends_count=F('friends_count')+1
friend_profile.save()
With Q filter and update
Profile.objects.filter(
Q(user=instance.request_id) |
Q(user=instance.friend_id)
).update(
friends_count=F('friends_count')+1
)
This last query uses the django.db.models Q object to make an SQL OR statement to retrieve the two profile instances; that of the requested and the requester: the update call acts on the query and updates all instances in the list

Lazy loading a model field's choices

I'm building a Django app to pull in data via an API to track live results of an event with the added ability to override that data before it is displayed.
The first task of the app is to make a request and store the response in the database so I've setup a model;
class ApiData(models.Model):
event = models.CharField(
_("Event"),
max_length=100,
)
key = models.CharField(
_("Data identifier"),
max_length=255,
help_text=_("Something to identify the json stored.")
)
json = JSONField(
load_kwargs={'object_pairs_hook': collections.OrderedDict},
blank=True,
null=True,
)
created = models.DateTimeField()
Ideally I would like it so that objects are created in the admin and the save method populates the ApiData.json field after creating an API request based on the other options in the object.
Because these fields would have choices based on data returned from the API I wanted to lazy load the choices but at the moment I'm just getting a standard Charfield() in my form.
Is this the correct approach for lazy loading model field choices? Or should I just create a custom ModelForm and load the choices there? (That's probably the more typical approach I guess)
def get_event_choices():
events = get_events()
choices = []
for event in events['events']:
choices.append((event['name'], event['title']),)
return choices
class ApiData(models.Model):
# Fields as seen above
def __init__(self, *args, **kwargs):
super(ApiData, self).__init__(*args, **kwargs)
self._meta.get_field_by_name('event')[0]._choices = lazy(
get_event_choices, list
)()
So I went for a typical approach to get this working by simply defining a form for the model admin to use;
# forms.py
from django import forms
from ..models import get_event_choices, ApiData
from ..utils.api import JsonApi
EVENT_CHOICES = get_event_choices()
class ApiDataForm(forms.ModelForm):
"""
Form for collecting the field choices.
The Event field is populated based on the events returned from the API.
"""
event = forms.ChoiceField(choices=EVENT_CHOICES)
class Meta:
model = ApiData
# admin.py
from django.contrib import admin
from .forms.apidata import ApiDataForm
from .models import ApiData
class ApiDataAdmin(admin.ModelAdmin):
form = ApiDataForm
admin.site.register(ApiData, ApiDataAdmin)

Effective Django database modeling

I want to model the following application: an Owner has different Shops and each Shop has some Customers and some Employees working for that Shop; the same Employee can work in different Shops belonging to the same Owner, but also in Shops belonging to different Owners.
Only Owner and Employee can login into the system, Customer can't login.
I created the following models and added users to different Groups (using Django Auth system and version 1.6.2 which allows custom user models), but I'm concerned with the number of query that the application is doing and I'm really not sure about the modeling.
The big difficulty is that, if the Owner has various Shops, when the Owner login into the system he needs to choose which Shop is working with, also to be able to add the related Employees and Customers (only the Owner of the Shop can add Employees and Customers)
from django.contrib.auth.models import AbstractBaseUser, PermissionsMixin
class CustomUser(AbstractBaseUser, PermissionsMixin):
email = models.CharField(max_length=254,
unique=True)
firstname = models.CharField(max_length=64)
lastname = models.CharField(max_length=64)
...
objects = CustomUserManager()
USERNAME_FIELD = 'email'
...
class Shop(models.Model):
name = models.CharField(...)
city = ...
address = ...
class Customer(models.Model):
shop = models.ForeignKey(Shop)
...
class Employee(CustomUser):
shops = models.ManyToManyField(Shop)
...
class Owner(CustomUser):
shops = models.ManyToManyField(Shop)
...
Now, when the Employee or the Owner login into the system with his email, the app needs to show a select box with the available shops, and the choice of the user need to pass to every view of the application: how do I do that? I suppose can't be a POST since I'll have other forms in the app, should be a GET request, but on every request I need to verify is the Shop belongs to the Owner or to the Employee (increasing number of queries). I already developed a big part of the application (order form for example) but I'm coming back to the beginning; I don't know if all the models I've done should be related to the Shop or to the Owner.
Any advice is appreciated. Thanks.
I have solved a similar problem using sessions and custom middleware based on Django's authentication middleware:
shop/middleware.py
from django.utils.functional import SimpleLazyObject
from <appname> import shop
def get_shop(request):
if not hasattr(request, '_cached_shop'):
request._cached_shop = shop.get_shop(request)
return request._cached_shop
class ShopMiddleware(object):
def process_request(self, request):
assert hasattr(request, 'session'), "The Shop middleware requires session middleware to be installed. Edit your MIDDLEWARE_CLASSES setting to insert 'django.contrib.sessions.middleware.SessionMiddleware'."
request.shop = SimpleLazyObject(lambda: get_shop(request))
shop/__init__.py
from django.core.exceptions import ObjectDoesNotExist
from <appname>.shop.models import Shop
SHOP_SESSION_KEY = '_session_shop_id'
def get_shop(request):
try:
shop_id = request.session[SHOP_SESSION_KEY]
shop = Shop.objects.get(id=shop_id)
return shop
except (KeyError, ObjectDoesNotExist):
return None
def switch_shop(request, shop):
if not isinstance(request.user, CustomUser):
request.session[SHOP_SESSION_KEY] = None
if request.user.shops.filter(id=shop.id).exists():
request.session[SHOP_SESSION_KEY] = shop.id
Then just add ShopMiddleware to your middleware classes, and request.shop will always point to the current shop if one is selected.
In my case I have also written a view wrapper similar to login_required that redirects to a page that allows selection of a shop whenever one is required and not selected. Take a look at login_required's source code for a good pointer in the right direction.
EDIT: You still need to select a shop, so write a view that presents the user with the right options, and let it call switch_shop(request, shop). If the shop is a valid shop for the current user, the session will be set to that shop until it is changed or the user logs out.
I now my example can be unperfect but i think it will clarify how you should use Django for this.
(Also read this: https://docs.djangoproject.com/en/1.6/topics/db/managers/)
class ShopsUser(AbstractBaseUser, PermissionsMixin):
email = models.CharField(max_length=254,
unique=True)
firstname = models.CharField(max_length=64)
lastname = models.CharField(max_length=64)
...
objects = CustomUserManager()
USERNAME_FIELD = 'email'
...
priviledge_flag = models.CharField(choices=(('o', 'owner'), ('e', 'employe'), ('c', 'customer'))
class Customer(models.Model):
shop = models.ForeignKey(Shop)
class Shop(models.Model):
customers = models.ForeignKey(Customer, related_name='shops')
admins = models.ManyToMany(ShopsUser, related_name='managed_shops')
Now you can find all data by using you logged in user (use sessions) in view:
class SomeView(View):
def get(self, *args, **kwargs):
admin = self.request.user
all_singed_in_admin_shops = admin.managed_shops.all()
first_shop = all_singed_in_admin_shops[0]
first_shop_customers = first_shop.customers.all()

Django social_auth custom user model FacebookBackend explanation

I'm trying to use social_auth (omab) for the first time and I'm find that there is no working example how to store basic facebook user data. Authentication works and user is created without problem as it's explained in the social_auth docs but I need to store gender and locale also. Both of them belongs to the basic facebook user data so they are in the facebook response all the time.
I'm use Django 1.4, Python2.7 and latest social_auth. So I was try to use SOCIAL_AUTH_USER_MODEL = 'myapp.UserProfile' in settings.py file and model.py is:
#!/usr/bin/python
#-*- coding: UTF-8 -*-
from django.db import models
from django.contrib.auth.models import User
from django.db.models import signals
import datetime
from datetime import timedelta
from django.db.models.signals import post_save
from social_auth.signals import pre_update
from social_auth.backends.facebook import FacebookBackend
class CustomUserManager(models.Manager):
def create_user(self, username, email):
return self.model._default_manager.create(username=username)
class UserProfile(models.Model):
gender = models.CharField(max_length=150, blank=True)
locale = models.CharField(max_length=150, blank=True)
#social_auth requirements
username = models.CharField(max_length=150)
last_login = models.DateTimeField(blank=True)
is_active = models.BooleanField()
objects = CustomUserManager()
class Meta:
verbose_name_plural = 'Profiles'
def __unicode__(self):
return self.username
def get_absolute_url(self):
return '/profiles/%s/' % self.id
def facebook_extra_values(sender, user,response, details, **kwargs):
profile = user.get_profile()
current_user = user
profile, new = UserProfile.objects.get_or_create(user=current_user)
profile.gender = response.get('gender')
profile.locale = response.get('locale')
profile.save()
return True
pre_update.connect(facebook_extra_values, sender=FacebookBackend, weak = False, dispatch_uid = 'facebook_extra_values_user')
In the settings.py I'm define pipeline
SOCIAL_AUTH_PIPELINE = (
'social_auth.backends.pipeline.social.social_auth_user',
#'social_auth.backends.pipeline.associate.associate_by_email',
'social_auth.backends.pipeline.user.create_user',
'social_auth.backends.pipeline.social.associate_user',
'social_auth.backends.pipeline.social.load_extra_data',
'social_auth.backends.pipeline.user.update_user_details',
'social_auth.backends.pipeline.misc.save_status_to_session',
)
but with above I get error AssertionError: ForeignKey(None) is invalid. First parameter to ForeignKey must be either a model, a model name, or the string 'self'
Also I was try to use AUTH_PROFILE_MODULE = 'myapp.UserProfile' instead as I was do before to extend user.model, which works well but don't understand how to populate needed data when UserProfile is created. Does anyone can place working code for this problem?
Thanks
There are a couple of ways to archive it, what fits better to your project is up to you of course, here's a list of available options:
Define this setting FACEBOOK_EXTRA_DATA = ('gender', 'locale'), the values will be available at the UserSocialAuth instance, to get them just do user.social_auth.get(provider='facebook').extra_data['gender'] or ['locale']. This is possible just because the information is available in the basic user data response.
Use a user profile to store this data (check django doc about it). Then just add a pipeline entry that stores the values in your profile instance.
Create a custom user model, SOCIAL_AUTH_USER_MODEL = 'myapp.CustomUser', and again add a custom pipeline entry that stores the values in your user instance.
Number 1 is not the best solution IMO, since a user can have several Facebook accounts connected and it could create a mess. Number 2 is good for Django 1.4 and lower, but it's deprecated starting from Django 1.5, something to take into account. Number 3 is a bit messy IMO.