2 Users in one model - django

New to Django. I'm trying to develop a Feedback module, but designing a database structure makes me confused for various reasons:
Where do I need to store feedback_score (positive/neutral/negative feedback ratio), should it be put in custom User, or somewhere else?
How should I get the recipient of the feedback credentials, should it be passed by URL, how to link recipient to FeedbackModel in class-based-views?
How to feedback_score to be calculated each time for every User?
models.py
User = get_user_model()
# Create your models here.
class FeedbackModel(models.Model):
id = models.AutoField(primary_key=False, db_column='id')
sender = models.ForeignKey(
User,
on_delete=models.CASCADE,
primary_key=False, related_name='feedback_left_by')
# recipient = models.ForeignKey(
# User,
# on_delete=models.CASCADE,
# primary_key=True)
FEEDBACK_OPTION = (
(-1, 'Negative'),
(0, 'Neutral'),
(+1, 'Good'),
)
feedback = models.IntegerField(choices=FEEDBACK_OPTION)
opinion = models.CharField(max_length=255)
class Meta:
ordering = ['left_by', '-id']
urls.py
from django.urls import path
from . import views
app_name = 'feedback'
urlpatterns = [
path('leave_feedback/<str:left_to>/',
views.leave_feedback.as_view(), name='leavefeedback'),
]
views.py
from django.shortcuts import render, reverse
from django.views.generic import TemplateView, CreateView
from django.contrib.auth.decorators import login_required
from django.utils.decorators import method_decorator
from .models import FeedbackModel
decorators = [login_required]
#method_decorator(decorators, name='dispatch')
class leave_feedback(CreateView):
model = FeedbackModel
fields = ['feedback', 'opinion']
success_url = '/'
#template_name = "feedback/leave_feedback.html"
def form_valid(self, form):
form.instance.sender = self.request.user
# ?
return super().form_valid(form)
custom user
class User(AbstractUser):
(...)
#property
def feedback_score(self)
return ???

feedback_score should be a class method in FeedbackModel, not a field. In general, you do not want fields in your database that can directly be derived from other fields. For instance:
class Person(models.Model):
name = models.CharField()
birthdate = models.DateField()
zodiac_sign = models.CharField()
If i have the birth date, I can easily get the zodiac sign. So, zodiac_sign should be a method. The same applies to your case. Just create a method that returns summary information on the feedback.

Where do I need to store feedback_score (positive/neutral/negative feedback ratio), should it be put in custom User, or somewhere else?
I think you current Model design is okay with the requirement. Just fix the ordering in class Meta.
BTW, if you want the user to have only one feedback, then consider using OneToOneField. If you want ratio of Feedback, just do like this:
feedbacks = Feeback.objects.all()
positive_feedbacks = feedbacks.filter(feedback=1).count() # returns count of the feedback
negative_feedbacks = feedbacks.filter(feedback=-1).count() # returns count of the feedback
neutral_feedbacks = feedbacks.filter(feedback=0).count() # returns count of the feedback
print("Ratio Positive:Negative:Neutral = {}:{}:{}".format(positive_feedbacks/negative_feedbacks, 1, neutral_feedbacks/negative_feedbacks)
How should I get the recipient of the feedback credentials, should it be passed by URL, how to link recipient to FeedbackModel in class-based-views?
You need to make the user login to the system. For example you can follow this tutorial but there are other good examples out there. Once you login into the system, the user is available via request.user. And your current implement reflects this as well.
How to feedback_score to be calculated each time for every User?
You can get the latest feedback by:
user = User.objects.get(id=1) # or user = request.user
latest_feedback = user.feedback_left_by.last()

Related

Querying Django model object

I can query the user's profile using the user_id in the UserProfile model below. But I would like to use the username in the django.contrib.auth.models.User.username in the urls instead.
The urls.py currently looks like:
path('profile/<int:user_id>', login_required(views.UserProfileDetail), name='userprofile'),
So as against the above urls, I would like to use something like:
path('profile/<str:username>', login_required(views.UserProfileDetail), name='userprofile'),
Any idea how to write the query correctly? All attempts so far returns an error
views.py:
def UserProfileDetail(request, user_id, *args, **kwargs):
profile = UserProfile.objects.get(user_id = user_id)
profile_display = {'profiledetail': profile }
return render(request, 'main/userprofile.html', context=profile_display)
this is what the model looks like:
from django.contrib.auth.models import User
class UserProfile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='userprofile')
userPhone = models.IntegerField(blank=True, null=True)
location = models.CharField(max_length=100, blank=True, null=True)
You can accept both a user_id and a username with a default value, and then query with Q objects to look for a UserProfile where at least one of the two matches. The paths guarantee that one is passed:
from django.contrib.auth.decorators import login_required
from django.db.models import Q
#login_required
def UserProfileDetail(request, user_id=None, username=None):
profile = UserProfile.objects.get(
Q(user_id=user_id) | Q(user__username=username)
)
profile_display = {'profiledetail': profile }
return render(request, 'main/userprofile.html', context=profile_display)
You might want to use login_required as a decorator instead. This avoids wrapping the view each time in a login_required.
Note: Functions are normally written in snake_case, not PerlCase, therefore it is
advisable to rename your function to user_profile_detail, not UserProfileDetail.
Note: It is normally better to make use of the settings.AUTH_USER_MODEL [Django-doc] to refer to the user model, than to use the User model [Django-doc] directly. For more information you can see the referencing the User model section of the documentation.

Using django-rest-auth registration with multiple user types

I'm actually learning Django Rest Framework and I have this project that involves multiple user types so for example it consists of Students, Teachers and Admins each one with his attributes so I created a User model that inherits from AbstractUser and 3 other models for each type where each type has a foreign key of it's corresponding user. So here comes the problem how can I use Django-Rest-Auth library with these user types so for example when I use the register endpoint how can I modify it in order to create a Student in the students model based on the request data ?
I would appreciate any ones help.
Thanks in advance
I would go with something like this for REST. So in my models,
class CustomUser(AbstractUser):
CHOICES = (
('T', 'Teacher'),
('A', 'Administration'),
('S', 'Student'),
)
role = models.CharField(max_length=1, choices=CHOICES)
class Teacher(models.Model):
user = models.OneToOneField(
CustomUser, on_delete=models.CASCADE, related_name="teacher_account"
)
...
class Student(models.Model):
...
Once your models are up and your settings updated to use the custom AUTH_USER_MODEL, create a custom registration serializer.
from rest_framework import serializers
from allauth.account.adapter import get_adapter
from allauth.account.utils import setup_user_email
from rest_auth.registration.serializers import RegisterSerializer
class MyCustomRegistrationSerializer(RegisterSerializer):
CHOICES = (
('T', 'Teacher'),
('A', 'Administration'),
('S', 'Student'),
)
role = serializers.ChoiceField(max_length=1, choices=CHOICES)
def get_cleaned_data(self):
data_dict = super().get_cleaned_data()
data_dict['role'] = self.validated_data.get('role', '')
return data_dict
Note the difference,serializers.ChoiceField.
Go ahead and tell django to use this during registration.So in your settings.py:
# note the path, use the location of your current serializers
REST_AUTH_REGISTER_SERIALIZERS = {
'REGISTER_SERIALIZER': 'api.serializers.MyCustomRegistrationSerializer',
}
Now, your local path for registering users should show an additional choice filed so that whenever a new user registers, they specify what role they have.
To save this information to the database during registration, however, create an adapter. So in your project application, say app3/adapter.py:
from allauth.account.adapter import DefaultAccountAdapter
class CustomAccountAdapter(DefaultAccountAdapter):
def save_user(self, request, user, form, commit=False):
user = super().save_user(request, user, form, commit)
data = form.cleaned_data
user.preferred_locale = data.get('role')
user.save()
return user
Likewise, register this adapter in your settings.
ACCOUNT_ADAPTER = 'app3.adapter.CustomAccountAdapter'
This should work. Have not tested it out for your particular use case though.

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)

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.

Django multiple User profiles/subprofiles

I am trying to create an intranet/extranet with internal/external user-specific profiles, with a common generic profile. I've looked at several answers on this site, but none specifically address what I'm looking to do. Below are the (stripped down) files I have so far.
What's the best way to create a profile model, with subprofiles for each user type? I'm trying not to require a custom authentication backend if at all possible.
https://gist.github.com/1196077
I have a solution I dont Know if its the best but see it:
models.py
from django.db import models
from django.contrib.auth.models import User
class Pollster(models.Model):
"""docstring for Polister"""
user = models.OneToOneField(User, related_name = 'polister', unique=True)
cedule = models.CharField( max_length = 100 )
class Respondent(models.Model):
""" """
born_date = models.DateField( verbose_name=u'fecha de nacimiento' )
cedule = models.CharField( max_length = 100, verbose_name=u'cedula' )
comunity = models.CharField( max_length = 100, verbose_name=u'comunidad')
phone = models.CharField( max_length = 50, verbose_name=u'telefono')
sanrelation = models.TextField( verbose_name =u'Relacion con SAN')
user = models.OneToOneField( User, related_name = 'respondent')
I create a MiddleWare: so
i create middleware.py
from django.contrib.auth.models import User
from encuestas.models import Pollster, Respondent
class RequestMiddleWare(object):
"""docstring for """
def process_request(self,request):
if isPollster(request.user):
request.user.userprofile = Pollster.objects.get( user = request.user.id)
elif isRespondent(request.user):
request.user.userprofile = Respondent.objects.get(user = request.user.id)
return None
def isPollster(user):
return Pollster.objects.filter(user=user.id).exists()
def isRespondent(user):
return Respondent.objects.filter(user=user.id).exists()
and you need to configure settings.py for the middleware:
add to MIDDLEWARE_CLASSES atribute:
'encuestas.middleware.RequestMiddleWare'
encuestas is my_app name
middleware is the Middleware file
RequestMiddleWare is the middleware class
You need a combination of storing additional information about users and model inheritance.
Basically, you'll need the generic User models we all know and either love or hate, and then you need a generic profile model that is your AUTH_PROFILE_MODULE setting.
That profile model will be a top-level model, with model subclasses for internal and extrernal users. You probably don't want an abstract model in this case since you'll need a common profile table to load user profiles from with User.get_profile().
So...I think the major thing you want to change is to make your Associate, External, etc. models inherit from your Profile model.
Please check this excellent article that describes how to inherit from the User class and add your own information. For me, at least, this clearly seems to be the way to go: http://scottbarnham.com/blog/2008/08/21/extending-the-django-user-model-with-inheritance/
Using this method one should easily be able to add multiple user types to their Django application.