Use autocomplete_fields with Proxy model - django

I want to implement autocomplete_fields feature but it doesn't work. I assume it happens because of Proxy model.
So I have Customer Proxy model and PromoCode model. PromoCode has FK to Customer model. And I need to have search field for customers in PromoCode change form. Here are models and admin classes:
class User(AbstractUser):
# bunch of fields
class Customer(User):
class Meta:
proxy = True
class CustomerAdmin(admin.ModelAdmin):
search_fields = ['email',]
admin.site.register(Customer, CustomerAdmin)
class PromoCode(TimeStampedModel):
customer = models.ForeignKey(User, on_delete=PROTECT, null=True, blank=True)
class PromoCodeAdmin(admin.ModelAdmin):
autocomplete_fields = ('customer',)
admin.site.register(PromoCode, PromoCodeAdmin)
This code gives error:
: (admin.E039) An admin for model "User" has to be registered to be referenced by PromoCodeAdmin.autocomplete_fields.
But I can't change model in customer field to Customer, becase when I run migration it breaks with following error:
ValueError: The field coupons.PromoCode.customer was declared with a lazy reference to 'users.customer', but app 'users' doesn't provide model 'customer'
Also I can't register User as admin class, because I don't need it to be registered. I register Customer model.
What can I do to solve such case?

It is not possible (see: https://code.djangoproject.com/ticket/30666). I got around this by registering the User admin, but making it redirect to my custom admin model. I also removed all of the actions on the user admin:
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
from django.contrib.auth.models import User
from django.http import HttpResponseRedirect
from django.urls import reverse
admin.site.unregister(User)
#admin.register(User)
class UserAdmin(BaseUserAdmin):
preserve_filters = False
def get_actions(self, request):
actions = super().get_actions(request)
if "delete_selected" in actions:
del actions["delete_selected"]
return actions
def has_delete_permission(self, request, obj=None):
return False
def has_add_permission(self, request):
return False
def has_change_permission(self, request, obj=None):
return False
def changelist_view(self, *args, **kwargs):
return HttpResponseRedirect(reverse("admin:core_domainuser_changelist"))

Related

Use custom UserCreationForm with GeoDjango admin (OSMGeoAdmin)

Django==2.2.1
GDAL==2.3.2
django-username-email==2.2.4
I have a simple Django application with a custom user model based on django-username-email's AbstractCUser, which removes the username from the user model, using e-mail address instead. On the user model, I defined a PointField field storing the user's current location.
models.py
from django.contrib.gis.db import models as gis_models
from cuser.models import AbstractCUser
class User(AbstractCUser):
"""Custom user model that extends AbstractCUser."""
current_location = gis_models.PointField(null=True, blank=True,)
I would like to register this model in Django admin so that I can register new users and view/set their location with a map widget. This kind of works if I use a custom user admin based on admin.OSMGeoAdmin in combination with a custom user change form:
admin.py
from django.contrib.gis import admin
from django.contrib.auth import get_user_model
from .forms import CustomUserCreationForm, CustomUserChangeForm
class CustomUserAdmin(admin.OSMGeoAdmin):
model = get_user_model()
add_form = CustomUserCreationForm # <- there seems to be a problem here
form = CustomUserChangeForm
list_display = ['email', 'last_name', 'first_name']
readonly_fields = ['last_login', 'date_joined']
admin.site.register(get_user_model(), CustomUserAdmin)
forms.py
from django.contrib.auth import get_user_model
from django.contrib.auth.forms import UserCreationForm, UserChangeForm
class CustomUserCreationForm(UserCreationForm):
class Meta(UserCreationForm.Meta):
model = get_user_model()
exclude = ('username',)
class CustomUserChangeForm(UserChangeForm):
class Meta(UserChangeForm.Meta):
model = get_user_model()
fields = (
'email',
'first_name',
'last_name',
'current_location',
# ...
)
When I open an existing user record in the Django admin, the required fields are displayed as intended, and the current location is displayed on a map. However, the same form seems to be used for user creation as well (i.e. add_form has no effect), which makes it impossible to add new users via the admin, because the password setting functionality is not embedded correctly (see screen shot).
The problem seems to be that OSMGeoAdmininherits from ModelAdmin, which in contrast to the standard UserAdmindoes not have an add_form property.
Is there any way to specify a custom user creation form in this case (ideally the UserCreationForm provided by django-username-email while maintaining the ability to display point fields on a map on the user change form?
You need to override get_form similar to how django.contrib.auth.admin.UserAdmin does.
def get_form(self, request, obj=None, **kwargs):
"""
Use special form during user creation
"""
defaults = {}
if obj is None:
defaults['form'] = self.add_form
defaults.update(kwargs)
return super().get_form(request, obj, **defaults)
Following schillingt's suggestion, this is the code I ended up using:
from django.contrib.gis import admin
from django.contrib.auth import get_user_model
from cuser.forms import UserCreationForm
from .forms import CustomUserChangeForm
class CustomUserAdmin(admin.OSMGeoAdmin):
model = get_user_model()
add_form = UserCreationForm
form = CustomUserChangeForm
list_display = ['email', 'last_name', 'first_name']
readonly_fields = ['last_login', 'date_joined']
def get_form(self, request, obj=None, **kwargs):
"""
Use special form during user creation.
Override get_form method in the same manner as django.contrib.auth.admin.UserAdmin does.
"""
defaults = {}
if obj is None:
defaults['form'] = self.add_form
defaults.update(kwargs)
return super().get_form(request, obj, **defaults)
admin.site.register(get_user_model(), CustomUserAdmin)

Django API: get authenticated user from request

The user is authenticated using allauth. I want to create a profile and set the authenticated user as the owner of the profile. How can I get the user?
Model class:
from django.db import models
from allauth.utils import get_user_model
from courses.models import Course
class Profile(models.Model):
owner = models.OneToOneField(get_user_model(), on_delete=models.CASCADE)
courses = models.ManyToManyField(Course, blank=True)
def get_courses_items(self):
return self.courses.all()
def __str__(self):
return self.owner.username
Views:
from rest_framework.generics import CreateAPIView
from profiles.models import Profile
from .serializers import ProfileSerializer
class ProfileCreateView(CreateAPIView):
queryset = Profile.objects.all()
serializer_class = ProfileSerializer
You can get the user from the request with request.user. Then probably you should override the def create method of the CreateAPIView to use that user and create the object.
set your user model in the settings file (base.py) and import it
AUTH_USER_MODEL = 'users.User' #(format is module.user model name)
from django.conf import settings
user = models. OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
method 2
Override the get_create method of the ProfileView. Every authenticated request has a request.user object in it which represents the user making the request. To get the user's id with this you just run request.user.id
class ProfileCreateView(CreateAPIView):
queryset = Profile.objects.all()
serializer_class = ProfileSerializer
def create(self, request, *args, **kwargs):
user = User.objects.get(id=request.user.id)
# create the profile and save it
...
in your serializers you can also get the current user this way
from rest_framework.serializers import CurrentUserDefault, PrimaryKeyRelatedField
class ProfileModelSerializer(ModelSerializer):
user = PrimaryKeyRelatedField(read_only=True, default=CurrentUserDefault())
class Meta:
...
I don't know how the remainder of your setup is but any or a combination of these works

How to apply/set a permission to an User

I have a Car model in my car_store app.
class Car(models.Model):
...
class Meta:
permissions = (('can_buy', 'User can buy this car'),)
Right now I have a custom User model in my accounts app which is:
from django.db import models as _models
from django.contrib.auth.models import AbstractBaseUser
class User(AbstractBaseUser):
email = models.EmailField()
....
def has_perms(self, perm, obj=None):
return True
def has_module_perms(self, app_label):
return True
and later I would simple make use of PermissionRequiredMixin for my view:
from django.contrib.auth.mixins import PermissionRequiredMixin
from django.views.generic import ListView
class CarListView(PermissionRequiredMixin, ListView):
permission_required = ('car.can_buy',)
The question is django does not offer any type of self.set_perm(self, perm) or set_perm(some_user, perm)? If not, what would be the simplest way to implement it?
Should I simply do something like this?
class User(AbstractBaseUser):
perms = []
def set_perm(self, perm):
self.perms.append(perm)
def has_perm(self, perm):
return self.perms.count(perm)
I couldn't find nothing that describes how to set a perm to an user (except third-party django-guardian - assign_perm()). Neither a Full Example from django docs talks about how to set a perm to the User. There's even this Answer, but he doesn't talk about setting the perm to the user, but just how to check for a perm.

Django user-to-groups in userAdmin, and group-to-users in groupAdmin

Is it possible to have many-to-many widget in admin-panel in user-tab to pick groups, that user belong to, and similar many-to-many widget in group-tab to pick users, which should belong to that group?
There's the easy way and the hard way.
The easy way is to use Django's InlineModelAdmin objects. This way, however, you cannot use the group widget.
from django.contrib.auth.admin import GroupAdmin
from django.contrib.auth.models import User, Group
class UserSetInline(admin.TabularInline):
model = User.groups.through
raw_id_fields = ('user',) # optional, if you have too many users
class MyGroupAdmin(GroupAdmin):
inlines = [UserSetInline]
# unregister and register again
admin.site.unregister(Group)
admin.site.register(Group, MyGroupAdmin)
The hard way requires you to build your own form, manually load and save the related users:
from django import forms
from django.contrib import admin
from django.contrib.auth.admin import GroupAdmin
from django.contrib.auth.models import User, Group
class GroupForm(forms.ModelForm):
users = forms.ModelMultipleChoiceField(
label='Users',
queryset=User.objects.all(),
required=False,
widget=admin.widgets.FilteredSelectMultiple(
"users", is_stacked=False))
class Meta:
model = Group
exclude = () # since Django 1.8 this is needed
widgets = {
'permissions': admin.widgets.FilteredSelectMultiple(
"permissions", is_stacked=False),
}
class MyGroupAdmin(GroupAdmin):
form = GroupForm
def save_model(self, request, obj, form, change):
# save first to obtain id
super(GroupAdmin, self).save_model(request, obj, form, change)
obj.user_set.clear()
for user in form.cleaned_data['users']:
obj.user_set.add(user)
def get_form(self, request, obj=None, **kwargs):
if obj:
self.form.base_fields['users'].initial = [o.pk for o in obj.user_set.all()]
else:
self.form.base_fields['users'].initial = []
return GroupForm
# unregister and register again
admin.site.unregister(Group)
admin.site.register(Group, MyGroupAdmin)

Extending django UserProfile, new field shows up as (None) after adding it to list_display

I'm extending the UserProfile but I'm having trouble getting the new field - current_article - that I added to list_display to show properly in the user overview page -
Home › Auth › Users
The new fields has it's own column but always with value (None) even after selecting values in the user detail page.
How can I get the field's values to show up in overview admin page?
I referenced this stackoverflow question:
Django Admin: how to display fields from two different models in same view?
Here is the code:
#admin.py
class UserProfileInline(admin.StackedInline):
model = UserProfile
class CustomUserAdmin(UserAdmin):
inlines = [
UserProfileInline,
]
def current_article(self,instance):
return instance.user.current_article
list_display = ('id','username','email','current_article','first_name','last_name','is_active', 'date_joined', 'is_staff','last_login','password')
admin.site.unregister(User)
admin.site.register(User, CustomUserAdmin)
And in Models.py
#models.py
from django.db import models
from django.contrib.auth.models import User
from django.db.models.signals import post_save
class UserProfile(models.Model):
user = models.OneToOneField(User)
current_article = models.ForeignKey(Article,blank=True,default=1)
def __unicode__(self):
return "{}".format(self.user)
def create_user_profile(sender, instance, created, **kwargs):
if created:
UserProfile.objects.create(user=instance)
post_save.connect(create_user_profile, sender=User)
Your method is actually raising an AttributeError exception, but Django hides this when processing list_display (It catches all exceptions and returns None as the value).
You need to have return instance.get_profile().current_article.