Django Admin: Ordering of ForeignKey and ManyToManyField relations referencing User - python

I have an application that makes use of Django's UserProfile to extend the built-in Django User model. Looks a bit like:
class UserProfile(models.Model):
user = models.ForeignKey(User, unique=True)
# Local Stuff
image_url_s = models.CharField(max_length=128, blank=True)
image_url_m = models.CharField(max_length=128, blank=True)
# Admin
class Admin: pass
I have added a new class to my model:
class Team(models.Model):
name = models.CharField(max_length=128)
manager = models.ForeignKey(User, related_name='manager')
members = models.ManyToManyField(User, blank=True)
And it is registered into the Admin:
class TeamAdmin(admin.ModelAdmin):
list_display = ('name', 'manager')
admin.site.register(Team, TeamAdmin)
Alas, in the admin inteface, when I go to select a manager from the drop-down box, or set team members via the multi-select field, they are ordered by the User numeric ID. For the life of me, I can not figure out how to get these sorted.
I have a similar class with:
class Meta:
ordering = ['name']
That works great! But I don't "own" the User class, and when I try this trick in UserAdmin:
class Meta:
ordering = ['username']
I get:
django.core.management.base.CommandError: One or more models did not validate:
events.userprofile: "ordering" refers to "username", a field that doesn't exist.
user.username doesn't work either. I could specify, like image_url_s if I wanted to . . . how can I tell the admin to sort my lists of users by username? Thanks!

This
class Meta:
ordering = ['username']
should be
ordering = ['user__username']
if it's in your UserProfile admin class. That'll stop the exception, but I don't think it helps you.
Ordering the User model as you describe is quite tricky, but see http://code.djangoproject.com/ticket/6089#comment:8 for a solution.

One way would be to define a custom form to use for your Team model in the admin, and override the manager field to use a queryset with the correct ordering:
from django import forms
class TeamForm(forms.ModelForm):
manager = forms.ModelChoiceField(queryset=User.objects.order_by('username'))
class Meta:
model = Team
class TeamAdmin(admin.ModelAdmin):
list_display = ('name', 'manager')
form = TeamForm

This might be dangerous for some reason, but this can be done in one line in your project's models.py file:
User._meta.ordering=["username"]

For me, the only working solution was to use Proxy Model. As stated in the documentation, you can create own proxy models for even built-in models and customize anything like in regular models:
class OrderedUser(User):
class Meta:
proxy = True
ordering = ["username"]
def __str__(self):
return '%s %s' % (self.first_name, self.last_name)
After that, in your model just change Foreign Key to:
user = models.OneToOneField(OrderedUser, unique=True)
or even more suitable
user = models.OneToOneField(OrderedUser, unique = True, parent_link = True)

Related

How to order readonly M2M fields in django admin

I can't seem to work out how to hook into the queryset of a readonly field in Django admin. In particular I want to do this for an inline admin.
# models.py
class Value(models.Model):
name = models.TextField()
class AnotherModel(models.Model):
values = models.ManyToManyField(Value)
class Model(models.Model):
another_model = models.ForeignKey(AnotherModel)
# admin.py
class AnotherModelInline(admin.TabularInline):
# How do I order values by 'name'?
readonly_fields = ('values',)
class ModelAdmin(admin.ModelAdmin):
inlines = (AnotherModelInline,)
Note that this could probably be done by overriding the form and then setting the widget to disabled, but that's a bit of a hack and doesn't look nice (I don't want greyed out multi-select, but a comma-separated list of words.
You can set an ordering metadata in the Values model:
class Value(models.Model):
name = models.TextField()
class Meta:
ordering = ['name']

Trying to extend AbstractUser to create multiple user types in Django

So I have been searching all around the internet for a full example of how to user AbstractUser when u have at least 2 different models. Didn't find anything conclusive.. at least that would work on latest version of Django (2.0.1).
I have 2 models, teacher and student, and registration needs to be different. Besides username, email, name and surname, I need for example, for the student, to upload a profile picture, email, phone, student_ID. And for teacher, bio, academic title and website. Did I start good ? What is the right approach ?
class Profile(AbstractUser):
photo = models.ImageField(upload_to='students_images')
email = models.EmailField()
phone = models.CharField(max_length=15, )
class Student(Profile):
student_ID = models.CharField(unique=True, max_length=14,
validators=[RegexValidator(regex='^.{14}$',
message='The ID needs to be 14 characters long.')])
def __str__(self):
return self.name
class Teacher(Profile):
academic_title = models.CharField(max_length=30)
bio = models.TextField()
website = models.URLField(help_text="E.g.: https://www.example.com", blank=True)
Your goals can be accomplished using a 'Profile' pattern. You don't necessarily need to use a custom user model for this. But you need to have a single common model to for authentication; you can use the builtin django user for this or a custom class... Your Student and Teacher models should be OnetoOne relationships. This is the recommended solution per the documentation.
If you wish to store information related to User, you can use a OneToOneField to a model containing the fields for additional information. This one-to-one model is often called a profile model, as it might store non-auth related information about a site user.
In your case, you may do something like this:
class StudentProfile(models.Model):
user = models.OneToOneField('User', related_name='student_profile')
# additional fields for students
class TeacherProfile(models.Model):
user = models.OneToOneField('User', related_name='teacher_profile')
# additional fields for teachers
Then you can create your registration forms based on these profile models.
class StudentResistrationForm(forms.ModelForm):
class Meta:
model = StudentProfile
fields = (...)
class TeacherRegistrationForm(forms.ModelForm):
class Meta:
model = TeacherProfile
fields = (...)
You can create the user instance to which the profile is related to at the same time you create the profile. You might do this with formsets, for example.
add
class Meta:
abstract = True
to profile model
and change AbstractUser to models.Model

Django rest framework displaying specific field of a model

In my model:
from django.contrib.auth.models import User
class Restaurant(models.Model):
manager = models.ForeignKey(User, on_delete=models.PROTECT,
null=True, blank=False, related_name="manager")
in my serializers.py
class RestaurantSerializer(CoreHyperlinkedModelSerializer):
class Meta:
model = Restaurant
in my views.py
class RestaurantViewSet(viewsets.ModelViewSet):
queryset = Restaurant.objects.order_by('id').all()
serializer_class = RestaurantSerializer
on my list:
the manager is displaying as <rest_framework.relations.PKOnlyObject object at 0x9f7040xbc208>
How can I display it as normal data like its username?
You want to use a 'SlugRelatedField'.
There are a few ways you can go, but if you just want to show a username, all you need is this
from rest_framework import serializers
class RestaurantSerializer(serializers.ModelSerializer):
manager = serializers.CharField(source="manager.username")
class Meta:
model = Restaurant
if you inherit from ModelSerializer and skip the manager field, it will use user PK as the value of the manager field by default.
a slightly more involved way would be to define a separate serializer for User and then embed it in RestaurantSerializer.
from rest_framework import serializers
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
class RestaurantSerializer(serializers.ModelSerializer):
manager = UserSerializer()
class Meta:
model = Restaurant
And if you really want to use hyperlinked serializer, you need to do quite a bit of work. You need to read this part carefully http://www.django-rest-framework.org/api-guide/serializers/#how-hyperlinked-views-are-determined

django two ForeignKeys to same model - admin error

I have a Profiles app that has a model called profile, i use that model to extend the django built in user model without subclassing it.
models.py
class BaseProfile(models.Model):
user = models.OneToOneField(settings.AUTH_USER_MODEL, related_name='owner',primary_key=True)
supervisor = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='supervisor', null=True, blank=True)
#python_2_unicode_compatible
class Profile(BaseProfile):
def __str__(self):
return "{}'s profile". format(self.user)
admin.py
class UserProfileInline(admin.StackedInline):
model = Profile
class NewUserAdmin(NamedUserAdmin):
inlines = [UserProfileInline ]
admin.site.unregister(User)
admin.site.register(User, NewUserAdmin)
admin
the error is
<class 'profiles.admin.UserProfileInline'>: (admin.E202) 'profiles.Profile' has more than one ForeignKey to 'authtools.User'.
obviously i want to select a user to be a supervisor to another user. I think the relationship in the model is OK, the one that's complaining is admins.py file. Any idea ?
You need to use multiple inline admin.
When you have a model with multiple ForeignKeys to the same parent model, you'll need specify the fk_name attribute in your inline admin:
class UserProfileInline(admin.StackedInline):
model = Profile
fk_name = "user"
class SupervisorProfileInline(admin.StackedInline):
model = Profile
fk_name = "supervisor"
class NewUserAdmin(NamedUserAdmin):
inlines = [UserProfileInline, SupervisorProfileInline]
Django has some documentation on dealing with this: https://docs.djangoproject.com/en/1.9/ref/contrib/admin/#working-with-a-model-with-two-or-more-foreign-keys-to-the-same-parent-model
Here is an example that I have just tested to be working
class Task(models.Model):
owner = models.ForeignKey(User, related_name='task_owner')
assignee = models.ForeignKey(User, related_name='task_assigned_to')
In admin.py
class TaskInLine(admin.TabularInLine):
model = User
#admin.register(Task)
class MyModelAdmin(admin.ModelAdmin):
list_display = ['owner', 'assignee']
inlines = [TaskInLine]

Is it possible to add authentication on the basis of single model?

models.py:
import datetime
from django.db import models
from pygments.lexers import get_all_lexers
LEXERS = [item for item in get_all_lexers() if item[1]]
class Classname(models.Model):
class_name = models.CharField(max_length=8)
def __str__(self):
return self.class_name
class Sectionname(models.Model):
class_name = models.ForeignKey(Classname)
section_name = models.CharField(max_length=1, default='A')
def __str__(self):
return self.section_name
class Teachername(models.Model):
classname = models.ForeignKey(Classname, verbose_name='class Name')
secname = models.ForeignKey(Sectionname, verbose_name='sectionname')
teachname = models.CharField(max_length=50, verbose_name='teacher Name')
def __str__(self):
return self.teachname
class Attendancename(models.Model):
teacher_name = models.ForeignKey(Teachername)
date = models.DateField('Date')
intime = models.TimeField('IN-TIME')
outtime = models.TimeField('OUT-TIME')
def hours_conversion(self):
tdelta = (datetime.datetime.combine(datetime.date.today(),self.outtime) - datetime.datetime.combine(datetime.date.today(),self.intime))
hours, minutes = tdelta.seconds//3600, (tdelta.seconds//60)%60
return '{0}hrs {1}mins'.format(hours, minutes)
def __str__(self):
return "%s" %self.teacher_name
serializers.py:
from django.forms import widgets
from rest_framework import serializers
from .models import Classname, Sectionname, Teachername, Attendancename
class ClassSerializer(serializers.ModelSerializer):
class Meta:
model = Classname
fields = ('id', 'class_name',)
class SectionSerializer(serializers.ModelSerializer):
class Meta:
model = Sectionname
fields = ('id', 'class_name', 'section_name')
class TeacherSerializer(serializers.ModelSerializer):
class Meta:
model = Teachername
fields = ('id', 'classname', 'secname', 'teachname')
class AttendanceSerializer(serializers.ModelSerializer):
class Meta:
model = Attendancename
fields = ('id', 'teacher_name', 'date', 'intime', 'outtime')
I want to add owner field in my models to enforce DRF authentication system. Is it necessary to add 'owner' field to all my models above?
I'm following a tutorial on django-rest-framework I have several models as above. Is it possible to make a single model for this authentication purpose and to set permissions in serializers file as per that model & to access all models on the basis of that single model?
I want to add owner field in my models to enforce DRF authentication system
You don't need the owner field to "enforce" the authentication.
How to enable and handle authentication in DRF is explained here in all details: http://www.django-rest-framework.org/api-guide/authentication/
The owner field is only interesting for certain permission cases.
If you look at the docs, you'll see that DRF already ships with permissions for common cases like IsAuthenticated, IsAdmin and many more. Depending on the state of a user (logged in / logged out) he or she might see certain resources or not.
However, if you want to set object level permissions you need the owner field (or something comparable).
How else can you tell if a certain user is really associated with a certain object?
For all this the default User model (from django.contrib.auth) should be completely sufficient and I see no need for any extra models assuming you use one of the standard Authentication Backends which set request.user to an instance of User.

Categories