How could I display parent object attribute in Django Admin? - python

At the moment I'm working on an e-commerce application.
It contains a sub-app called "blog".
The idea is that the superuser creates an account for the *Trainer.
And yeah, I already created a new AbstractUser
Trainer logins into his account and creates Post
I logged in here using my Trainer`s credentials
After I want the superuser to see WHO created post, but DjangoAdmin displays me admin`s email
How could I display the email of the 'creator' of the post in Django admin?
Code:
#models.py
class UserTrainer(AbstractUser):
email = models.EmailField(verbose_name='email', max_length=100, unique=True)
age = models.PositiveIntegerField(null=True)
info = RichTextField(blank=True, null=True)
image = models.ImageField(upload_to='media/stuff_images')
inst = models.URLField(blank=True)
REQUIRED_FIELDS = ['email', ]
def __str__(self):
return self.email
def get_email(self):
return self.object.email
class Post(models.Model):
DEFAULT_TRAINER_ID = 1
article = models.CharField(max_length=50, default='Article text')
slug = models.SlugField(max_length=30)
keywords = models.CharField(max_length=100)
text = RichTextField(blank=True, null=True)
trainer = models.ForeignKey(UserTrainer, on_delete=models.CASCADE, null=False, default=1)
def __str__(self):
return self.article
class Meta:
verbose_name = 'Post'
verbose_name_plural = 'Posts'
#admin.py
class CustomUserAdmin(UserAdmin):
model = UserTrainer
add_form = CustomUserCreationForm
fieldsets = (
*UserAdmin.fieldsets,
(
'TrainerInfo',
{
'fields': (
'age', 'info', 'image', 'inst',
)
}
)
)
admin.site.register(UserTrainer, CustomUserAdmin)
#admin.register(Post)
class PostAdmin(admin.ModelAdmin):
list_display = ('article', 'slug','trainer')
list_display_links = ('article',)
fields = ('article', 'slug', 'keywords', 'text',)
readonly_fields = ('trainer',)

The problem is that you are are not specifying user when you save your post, so you should override your save method in admin.py, try this (OFFICIAL DOCS):
admin.site.register(UserTrainer, CustomUserAdmin)
#admin.register(Post)
class PostAdmin(admin.ModelAdmin):
list_display = ('article', 'slug','trainer')
list_display_links = ('article',)
fields = ('article', 'slug', 'keywords', 'text',)
readonly_fields = ('trainer',)
def save_model(self, request, obj, form, change):
obj.trainer = request.user
super().save_model(request, obj, form, change)

Related

ERROR: admin.E108 and admin.E116 Django Framework Python

I got the these two errors below:
<class 'blog.admin.CommentAdmin'>: (admin.E108) The value of
'list_display[4]' refers to 'active', which is not a callable, an
attribute of 'CommentAdmin', or an attribute or method on
'blog.Comment'.
<class 'blog.admin.CommentAdmin'>: (admin.E116) The value of
'list_filter[0]' refers to 'active', which does not refer to a Field.
This is my models.py code:
from django.contrib.auth.models import User
# Create your models here.
STATUS = (
(0,"Draft"),
(1,"Publish")
)
class Post(models.Model):
title = models.CharField(max_length=200, unique=True)
slug = models.SlugField(max_length=200, unique=True)
author = models.ForeignKey(User, on_delete= models.CASCADE,related_name='blog_posts')
updatedOn = models.DateTimeField(auto_now= True)
content = models.TextField()
createdOn = models.DateTimeField(auto_now_add=True)
status = models.IntegerField(choices=STATUS, default=0)
class Meta:
ordering = ['-createdOn']
def __str__(self):
return self.title
class Comment(models.Model):
post = models.ForeignKey(
Post, on_delete=models.CASCADE, related_name='comments')
name = models.CharField(max_length=80)
email = models.EmailField()
body = models.TextField()
createdOn = models.DateTimeField(auto_now_add=True)
status = models.BooleanField(default=False)
class Meta:
ordering = ['createdOn']
def __str__(self):
return 'Comment {} by {}'.format(self.body, self.name)
This is my admin.py code:
from django.contrib import admin
from .models import Post, Comment
# Register your models here.
class PostAdmin(admin.ModelAdmin):
list_display = ('title', 'slug', 'status','createdOn')
list_filter = ("status", 'createdOn')
search_fields = ['title', 'content']
prepopulated_fields = {'slug': ('title',)}
#admin.register(Comment)
class CommentAdmin(admin.ModelAdmin):
list_display = ('name', 'body', 'post', 'createdOn', 'active')
list_filter = ('active', 'createdOn')
search_fields = ('name', 'email', 'body')
actions = ['approveComments']
def approveComments(self, request, queryset):
queryset.update(active=True)
admin.site.register(Post, PostAdmin)
This is my forms.py code:
from .models import Comment
from django import forms
class CommentForm(forms.ModelForm):
class Meta:
model = Comment
fields = ('name', 'email', 'body')
Any help is greatly appreciated.
The message is clear 'active' is not a field
class Comment(models.Model):
post = models.ForeignKey(
Post, on_delete=models.CASCADE, related_name='comments')
name = models.CharField(max_length=80)
email = models.EmailField()
body = models.TextField()
createdOn = models.DateTimeField(auto_now_add=True)
status = models.BooleanField(default=False)
your fields are: post, name, email, createdOn, status
Therefore create a field named active or suppress active in list_display &
list_filter
status = models.IntegerField(choices=STATUS, default=0)
should be:
active = models.IntegerField(choices=STATUS, default=0)
I got the same error below:
ERRORS: <class 'store.admin.PersonAdmin'>: (admin.E116) The value of
'list_filter[0]' refers to 'PersonAgeFilter', which does not refer to
a Field.
When assigning the custom filter PersonAgeFilter to list_filter() with parentheses as shown below:
# "store/admin.py"
from django.contrib import admin
from .models import Person
class PersonAgeFilter(admin.SimpleListFilter):
# ...
#admin.register(Person)
class PersonAdmin(admin.ModelAdmin):
list_filter = ("PersonAgeFilter",)
# ↑ Parentheses ↑
So, I removed the parentheses from "PersonAgeFilter" as shown below then the error was solved.
# "store/admin.py"
from django.contrib import admin
from .models import Person
class PersonAgeFilter(admin.SimpleListFilter):
# ...
#admin.register(Person)
class PersonAdmin(admin.ModelAdmin):
list_filter = (PersonAgeFilter,)
# Without parentheses

How to make autocomplete filter in django admin form with class based model

I want to add a dropdown with autocomplete filter such as select2 into django admin form with class based model. i have tried several tricks avilable over the internet but not succeeded. here are some code snippet i have. i want to show all category for a post which is already available into model.
in my model.py
class Post(models.Model):
category = models.ForeignKey(Category, on_delete=models.CASCADE)
title = models.CharField(max_length=200, unique=True)
slug = models.SlugField(max_length=200, unique=True)
featured_image = models.ImageField(null=True, blank=True, upload_to="blog/", verbose_name='Featured Image')
author = models.ForeignKey(User, on_delete= models.CASCADE,related_name='blog_posts')
updated_on = models.DateTimeField(auto_now= True)
content = RichTextUploadingField()
created_on = models.DateTimeField(auto_now_add=True)
status = models.IntegerField(choices=STATUS, default=0)
class Meta:
ordering = ['-created_on', 'title']
def __str__(self):
return self.title
def _generate_slug(self):
value = self.title
slug_candidate = slugify(value, allow_unicode=True)
self.slug = slug_candidate
def save(self, *args, **kwargs):
if not self.pk:
self._generate_slug()
super().save(*args, **kwargs)
my admin.py
class PostAdmin(admin.ModelAdmin):
list_display = ('title', 'slug', 'status', 'category', 'author','created_on')
list_filter = ("status",)
search_fields = ['title', 'content']
prepopulated_fields = {'slug': ('title',)}
actions = [export_as_csv_action("CSV Export", fields=['title','slug','author','featured_image','status','created_on','updated_on'])]
how my form looks into django-admin
please suggest anything how to add i filter for category dropdown filter with autocomplete.
In Django 2.0+ you can just add autocomplete_fields to the ModelAdmin:
class PostAdmin(admin.ModelAdmin):
autocomplete_fields = ['category']
This will add asynchronous search of all categories the user has access to (versus a standard dropdown/select).

Updating custom user (AbstractUser) in Django

I created a custom user using CBV and i have succeeded in implementing signup view however i am finding difficult to implement the update view. I have implemented the update view using the following code:
models.py
class CustomUser(AbstractUser):
state = models.CharField(choices=States, default ='abia', max_length=50 )
city = models.CharField(max_length=50)
local_government = models.CharField(max_length=50)
phone_number = models.CharField(max_length=50, blank= True)
picture = models.ImageField(upload_to='user/photos', blank=False, null=False, validators=
[clean_image])
forms.py
class CustomUserCreationForm(UserCreationForm):
class Meta:
model = CustomUser
fields = UserCreationForm.Meta.fields + ('state', 'city', 'local_government', 'phone_number',
'picture',)
class CustomUserChangeForm(UserChangeForm):
class Meta:
model = CustomUser
fields = UserCreationForm.Meta.fields + ('state', 'city', 'local_government', 'phone_number',
'picture',)
admin.py
class CustomUserAdmin(UserAdmin):
add_form = CustomUserCreationForm
form = CustomUserChangeForm
model = CustomUser
list_display = ['id', 'first_name', 'last_name', 'email','subscription_plan', 'state', 'city',
'local_government', 'phone_number', 'picture']
views.py (update):
class UpdateUser( LoginRequiredMixin, UpdateView):
model = CustomUser
form_class = CustomUserChangeForm
template_name = 'profile/profile-edit.html'
login_url = 'login'
The UpdateUser page just reload when i click to update profile without committing the update. Any help will be appreciated.
Since you are updating user object you should send the pk value to the URL tag in the template

Django admin - Hidden field - Cannot assign must be an instance

I have a CustomUser model with ForeignKey to another model. In my Signup form, I'm using a hidden field that is populated with a value depending on a search result from the user (the user is searching for a company and after clicking on the result the hidden input value represents that company's ID).
When I'm saving the User object I can easily get the ID, make a query and add the company object to the field.
The problem is with my Admin. The Admin is using the same form and the company field is rendered as hidden with the ID value.
When I try to change some User info and try to save it I get this error:
Cannot assign "'3'": "CustomUser.kompanija" must be a "Kompanija" instance.
I thought that the Admin is using the same save method from my custom UserAccountAdapter...?
Is it possible to override the hidden input just for the Admin to show:
forms.ModelChoiceField(queryset=Kompanija.objects.all(), empty_label="Kompanija")
with initial value from the saved user object?
My models.py:
class Kompanija(models.Model):
naziv = models.CharField(max_length=50)
adresa = models.CharField(max_length=50, blank=True)
def __str__(self):
return self.naziv
class CustomUser(AbstractUser):
ime = models.CharField(max_length=30, default='')
prezime = models.CharField(max_length=30, default='')
kompanija = models.ForeignKey(Kompanija, on_delete=models.CASCADE, null=True, blank=True)
is_premium = models.BooleanField('premium status', default=False)
def __str__(self):
return self.email
My forms.py:
class CustomUserCreationForm(UserCreationForm):
ime = forms.CharField(max_length=30, label='Ime')
prezime = forms.CharField(max_length=30, label='Prezime')
kompanija = forms.CharField(widget=forms.HiddenInput())
class Meta(UserCreationForm):
model = CustomUser
fields = ('username', 'email', 'ime', 'prezime')
class CustomUserChangeForm(UserChangeForm):
ime = forms.CharField(max_length=30, label='Ime')
prezime = forms.CharField(max_length=30, label='Prezime')
kompanija = forms.CharField(widget=forms.HiddenInput())
class Meta:
model = CustomUser
fields = ('username', 'email', 'ime', 'prezime')
class CustomSignupForm(SignupForm):
ime = forms.CharField(max_length=30, label='Ime')
prezime = forms.CharField(max_length=30, label='Prezime')
kompanija = forms.CharField(widget=forms.HiddenInput())
class Meta:
model = CustomUser
def signup(self, request, user):
user.ime = self.cleaned_data['ime']
user.prezime = self.cleaned_data['prezime']
user.kompanija = Kompanija.objects.get(id=self.cleaned_data['kompanija'])
user.save()
return user
My adapter.py:
class UserAccountAdapter(DefaultAccountAdapter):
def save_user(self, request, user, form, commit=True):
user = super(UserAccountAdapter, self).save_user(request, user, form, commit=False)
user.ime = form.cleaned_data.get('ime')
user.prezime = form.cleaned_data.get('prezime')
user.kompanija = Kompanija.objects.get(id=form.cleaned_data.get('kompanija'))
user.save()
My admin.py:
class CustomUserAdmin(UserAdmin):
add_form = CustomUserCreationForm
form = CustomUserChangeForm
model = CustomUser
list_display = ['email', 'ime', 'prezime', 'username', 'kompanija', 'is_premium']
fieldsets = (
(('Korisnik'), {'fields': ('email', 'ime', 'prezime', 'username', 'kompanija', 'is_premium')}),
)
admin.site.register(CustomUser, CustomUserAdmin)
admin.site.register(Kompanija)
My settings.py:
ACCOUNT_FORMS = {
'signup': 'korisnici.forms.CustomSignupForm',
}
ACCOUNT_ADAPTER = 'korisnici.adapter.UserAccountAdapter'
After some more research and crashing my site, I found a solution to this.
I needed to add ModelForm in my forms.py just for the Admin site.
In my forms.py I added:
class CustomUserAdminForm(forms.ModelForm):
class Meta:
model = CustomUser
fields = ('email', 'ime', 'prezime', 'username', 'kompanija', 'is_premium')
And my admin.py:
class CustomUserAdmin(UserAdmin):
form = CustomUserAdminForm
model = CustomUser
list_display = ['email', 'ime', 'prezime', 'username', 'kompanija', 'is_premium']
fieldsets = (
(('Korisnik'), {'fields': ('email', 'ime', 'prezime', 'username', 'kompanija', 'is_premium')}),
)
admin.site.register(CustomUser, CustomUserAdmin)
admin.site.register(Kompanija)
Now everything works as expected.

Django Admin Panel, Relations and ReadOnly fields

I have a very simple scheme in model.py
class Attachment(models.Model):
name = models.CharField(max_length=100,
verbose_name='name')
file = models.FileField(upload_to=settings.MEDIA_ROOT,
null=True,
verbose_name='file')
def __str__(self):
return self.name
class Document(models.Model):
title = models.CharField(max_length=250, blank=False)
attachment = models.ForeignKey('Attachment', null=True, on_delete=models.CASCADE)
date = models.DateField(blank=True)
approved = models.BooleanField(default=False)
def __str__(self):
return self.title
And my admin.py
class DocumentAdmin(admin.ModelAdmin):
fieldsets = (
('GENERAL', {
'fields': ('title', 'attachment', 'date', 'approved')
}),
)
admin.site.register(Document, DocumentAdmin)
There is two issues I'm struggling with:
Firstly, I would like to include Attachment's fields in DocumentAdmin interface. I've created a get method in Document model.
def get_attachment_file(self):
return self.attachment.file
Method get_attachment_file is working in list_display, but not in fieldset
list_display = ('get_attachment_file',)
In addition, I would like to make fields "approved" and "date" read only, after "approved" is set to "True".
Thank you all.
You should be able to add get_attachment_file and make it a readonly field.
For making approved and date readonly after approved is set to True you can use the get_readonly_fields method
def get_readonly_fields(self, request, obj=None):
readonly_fields = ('get_attachment_file',)
if obj and obj.approved:
readonly_fields += ('approved', 'date')
return readonly_fields

Categories

Resources