Display a model field as readonly in Django admin - python

I want to display a model field ie module_name as read only in django user profile edit page. The model Category has a manytomany relationship with Profile model.
Category
id category_name module_name
1 A X
2 B Y
profile_category
id profile_id category_id
1 2 1
So for eg. when I am on the admin page to editing a user id of 2 then I would like to display the module name X if category id 1 is assigned to the user id 2
models.py
class Category(models.Model):
category_name = models.CharField(max_length=150, blank=False, null=True, default="", unique=True)
module_name = models.TextField(blank=False, null=True)
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, related_name ='profile')
phone_number = PhoneNumberField( blank=True, null=True)
organisation = models.CharField(max_length=30, blank=True)
category = models.ManyToManyField(Category)
admin.py
class ProfileInline(admin.StackedInline):
model = Profile
filter_horizontal = ('category',)
class CustomUserAdmin(UserAdmin):
inlines = (ProfileInline, )
list_select_related = ( 'profile', )
Please suggest if this is possible or not.
Any help/suggestion is highly appreciated. Thanks in advance.

Method 1
Use readonly_fields which is inherited from admin.BaseModelAdmin.
class ProfileInline(admin.StackedInline):
model = Profile
readonly_fields = ('module_names',)
def module_names(self, instance):
# Write a get-method for a list of module names in the class Profile
# return HTML string which will be display in the form
return format_html_join(
mark_safe('<br/>'),
'{}',
((line,) for line in instance.get_module_names()),
) or mark_safe("<span>The user belongs to no category</span>")
# short_description functions like a model field's verbose_name
module_names.short_description = "Module names"
Method 2
For older versions of admin site, you can hook the get_fieldsets method of admin.StackedInline. This method was posted here with a nice example.

Related

Django admin reverse foregin key relationship

I have a number of different models connected to the User model through a foregin key relationship. I would now like to display all the attributes from the objects connected to the User model in the main admin overview.
models.py
class User(AbstractBaseUser):
username = models.CharField(max_length=60)
lastname = models.CharField(max_length=60, blank=True)
class UserProfile(models.Model):
user_id = models.OneToOneField(
User,
on_delete=models.CASCADE,
primary_key=True,
)
address = models.CharField(max_length=60, blank=True)
admin.py
class UserProfileAdmin(admin.ModelAdmin):
list_display = ('user_firstname', 'user_lastname', 'address')
def user_firstname(self, instance):
return instance.user_id.username
def user_lastname(self, instance):
return instance.user_id.lastname
admin.site.register(UserProfile, UserProfileAdmin)
The code above works perfectly well to display attributes from "User" in "Userprofile", but how do I do this the other way around? In my code I currently have 4 different objects connected to the User object so keen to find a way to display all the data there.
use inlines
models.py
class Author(models.Model):
name = models.CharField(max_length=100)
class Book(models.Model):
author = models.ForeignKey(Author, on_delete=models.CASCADE)
title = models.CharField(max_length=100)
admin.py
class BookInline(admin.TabularInline):
model = Book
class AuthorAdmin(admin.ModelAdmin):
inlines = [
BookInline,
]
more info read https://docs.djangoproject.com/en/3.1/ref/contrib/admin/#inlinemodeladmin-objects

django all fields not appearing in admin form

I have following model in django:
class ProjectScheme(models.Model):
name = models.CharField(max_length=250, blank=False,null=False)
parent_scheme_id = models.ForeignKey(ProjectSchemeMaster, on_delete = models.CASCADE)
rule = models.TextField(blank=True)
created_on = models.DateTimeField(auto_now=False, auto_now_add=True)
created_by = models.IntegerField(blank=True,null=True)
updated_on = models.DateTimeField(auto_now=True, auto_now_add=False)
updated_by = models.IntegerField(blank=True,null=True)
def __str__(self):
return str(self.name)
And in admin.py
# Register your models here.
class ProjectSchemeAdmin(admin.ModelAdmin):
exclude = ('id',)
class Meta:
model = ProjectScheme
admin.site.register(ProjectScheme, ProjectSchemeAdmin)
But date fields: updated_on, created_on don't show up on admin form.
I also tried without ProjectSchemeAdmin class, and also:
class ProjectSchemeAdmin(admin.ModelAdmin):
pass
Also,
# list_display = [field.name for field in ProjectScheme._meta.fields if field.name != "id"]
list_display = ["id", "name","parent_scheme_id","rule","created_on", "created_by","updated_on","updated_by"]
But the same.
I need to get all the fields in admin form.
You need to add readonly_fields = ('created_on','updated_on') to display the date field.
# Register your models here.
class ProjectSchemeAdmin(admin.ModelAdmin):
list_display = ["name","parent_scheme_id","rule","created_on", "created_by","updated_on","updated_by"]
readonly_fields = ('created_on','updated_on')
class Meta:
model = ProjectScheme
admin.site.register(ProjectScheme, ProjectSchemeAdmin)
As mentioned in the docs:
As currently implemented, setting auto_now or auto_now_add to True will cause the field to have editable=False and blank=True set.
The value auto_now_add is True. So no matter what date value you enter via admin UI, it will ultimately be overriden, which means there is no reason to display the field. In this case, as you need to display the field anyway, adding the readonly_fields does the trick.
Hope this helps!

How to save data from form field in Model with One to One reltionship with a Model inheriting from AbstractUser

I have a field called org model called ScrummyUser with a one to one field with a model called User which inherits from AbstractUser model, how do I save data from the form field into the scrummyuser model
This is the Organization Model
class Organization(models.Model):
organization = models.CharField(max_length=255, null=True)
def __str__(self):
return self.organization
This is the ScrummyUser model
class ScrummyUser(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, primary_key=True, related_name='scrummy_profile')
role= models.CharField(max_length=100, choices=role, blank=True, null=True, default='DEV')
org = models.ForeignKey(Organization, max_length=255, on_delete=models.CASCADE, default=1)
This is the ScrummySignup form
class ScrummySignUpForm(UserCreationForm):
role = forms.ChoiceField(choices=role, required=False)
org = forms.ModelChoiceField(
queryset=Organization.objects.all(),
widget=forms.Select
)
class Meta(UserCreationForm.Meta):
model = User
fields = ['first_name', 'last_name','username' ,'email']
#transaction.atomic
def save(self):
user = super().save(commit=False)
user.is_user = True
user.save()
scrummy = ScrummyUser.objects.create(user=user, role=role)
return user
It throws me this errormessage
scrummy = ScrummyUser.objects.create(user=user, role=role, org=org)
NameError: name 'org' is not defined
which is obvious but I am looking for another approach to save the data
for access CutomUser with Abstract you should get User of settings:
from django.conf import settings
class ScrummyUser(models.Model):
user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE,
primary_key=True, related_name='scrummy_profile')
AbstractUser link
ok so I figured this out, after defining a variable org as a modelchoicefield, i passed it unto the fields for user model as a user form field before saving it into the scrummyuser model.

Django Admin Fk grouping

How do I go about grouping a set of fields within the Django Admin. By this I mean
Table 1: User - User Key - User Name
Table 2: Post - Post Key - User Key (FK)
In the PostAdmin I would like to display the User Name of the Author also perform an action on the posts. This works but displays the user name for each post created by the user.
Is there a way I could just display the user name once but in the action update all posts created by the user? - Update all posts with is_sealed = true.
Model
class User(AbstractBaseUser, PermissionsMixin):
email = models.EmailField(_("email address"), unique=True)
first_name = models.CharField(
_("first name"), max_length=50,
validators=[RegexValidator(
regex_alpha, code='invalid_first_name',
message=regex_alpha_message,
)]
)
middle_name = models.CharField(
_("middle name"), max_length=50, blank=True,
null=True, default=None,
validators=[RegexValidator(
regex_alpha, code='invalid_middle_name',
message=regex_alpha_message,
)]
)
class Post(models.Model):
owner = models.ForeignKey(settings.AUTH_USER_MODEL)
title = models.CharField(_('title'), max_length=250)
label = models.ForeignKey(Label, verbose_name=_('label'))
significance = models.CharField(max_length=3, choices=SIGNIFICANCE_CHOICES)
is_sealed = models.BooleanField(_('Is Sealed?'), default=False)
event_date = models.DateField()
message = models.TextField(_("Message"), blank=True, default='')
PostAdmin (I need to fix the issue here)
class PostAdmin(admin.ModelAdmin):
list_display = ['owner','is_sealed']
ordering = ['owner']
actions = [open_vault]
search_fields = ['owner__email',]
list_filter = ['owner__email']
So, according to your comments, you must write a custom method inside UserAdmin for displaying posts related to a user/author, like this:
from django.utils.html import mark_safe
class UserAdmin(admin.ModelAdmin):
list_display = ['email', 'get_posts']
# ... other settings here
def get_posts(self, obj):
to_return = '<ul>'
for post in obj.post_set.all():
to_return += '<li>{}</li>'.format(post.title)
to_return += '</ul>'
return mark_safe(to_return)

Django admin InlineModels for manytomany fields

I have designed following models for my blog
from django.db import models
class Post(models.Model):
title = models.CharField(max_length=200)
body = models.TextField(default='')
created_at = models.DateTimeField('created date', auto_now_add=True, auto_now=False)
updated_at = models.DateTimeField('updated date', auto_now_add=False, auto_now=True)
author = models.ForeignKey('Author', default='admin')
def __str__(self):
return self.title
class Author(models.Model):
name = models.CharField(max_length=150)
email = models.EmailField(blank=True)
bio = models.TextField()
def __str__(self):
return self.name
class Category(models.Model):
cat_name = models.CharField(max_length=200)
post = models.ManyToManyField('Post')
def __str__(self):
return self.cat_name
class Tag(models.Model):
tag_name = models.CharField(max_length=200)
post = models.ManyToManyField('Post')
def __str__(self):
return self.tag_name
and I am trying to register this model under django admin in such a way that. I can edit the Category, Tags and Authors from the Post page. but I am having hard time to accomplish this talk, I have written this code in admin.py file
from django.contrib import admin
from .models import Post, Author, Tag, Category
class AuthorInline(admin.TabularInline):
model= Author
class TagInline(admin.StackedInline):
model= Tag
class CategoryInline(admin.StackedInline):
model = Category
#admin.register(Post) #another method of registration admin.site.register(Post, PostAdmin)
class PostAdmin(admin.ModelAdmin):
#Show the following fields in this order
fields = ['body', 'title']
#show the following filelds for nice formattng
list_display = ['title', 'author', 'created_at']
#display based on the date hirerarchy
date_hierachy = 'created_at'
#embed the following child models in this parent models
inlines = [AuthorInline, TagInline, CategoryInline,]
#to exclude fields
exclude = ('author',)
When I run my server I got the errors like
ERRORS:
<class 'blogs.admin.AuthorInline'>: (admin.E202) 'blogs.Author' has no ForeignKey to 'blogs.Post'.
<class 'blogs.admin.CategoryInline'>: (admin.E202) 'blogs.Category' has no ForeignKey to 'blogs.Post'.
<class 'blogs.admin.TagInline'>: (admin.E202) 'blogs.Tag' has no ForeignKey to 'blogs.Post'.
when investigating the error, we cannot have StackedInline class if the models doesn't have foreign key, but How can I put the Tags, Category and Author rendered formm under the Post page in django admin,
For using AuthorInline, you ned a foreignkey field in you Author model
ex:
class Author(models.Model):
post = models.ForeignKey('Post')
This means one post may have multiple authors.
But here in your situation you have the correct model and fileds which have one author for one post, so you can remove AuthorInline.
And incase of Tag and Category, you are using many-to-many field, It will be good if you go through this documentation https://docs.djangoproject.com/en/dev/ref/contrib/admin/#working-with-many-to-many-models
You have to rewrite the CategoryInline and TagInline;
class TagInline(admin.StackedInline):
model= Tag.post.through
class CategoryInline(admin.StackedInline):
model = Category.post.through
This isn't what inlines are for, and you don't want them here.
Inlines are for the reverse relation: given an author, edit their details and enter all their books on the same page. Your foreign keys and many-to-many fields are best shown as simple widgets, which is what Django does by default; the author and category will be displayed as a dropdown allowing you to choose an item, and the tags will be displayed as a multi-select box.
You might also choose to register Book as an inline on the Author admin; that's up to you.
Finally I made, what I wanted, the main gist is to make the category, author and tags choosable from the post page, so to do that, we need to add all the fields in the post model, which is the modified model
from django.db import models
from django.utils import timezone
class Author(models.Model):
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
email = models.EmailField(blank=True)
bio = models.TextField()
class Tag(models.Model):
tag_name = models.CharField(max_length=50)
class Category(models.Model):
cat_name = models.CharField(max_length=50)
class Post(models.Model):
'''post can have many categories
and categories can have many post
author can have many post but post
can have single author
post can have many tags, and tags
can have many posts'''
title = models.CharField('post title', max_length=200)
body = models.TextField(default='', null=True)
created_at = models.DateTimeField(auto_now_add=True, auto_now=False)
updated_at = models.DateTimeField(auto_now_add=False, auto_now=True)
author = models.ForeignKey(Author, verbose_name = "List of Author") #many to one relationship
def __str__(self):
return self.title
#Generally many to many fields should into that model which is going to be edited.
tags = models.ManyToManyField(Tag)
categories = models.ManyToManyField(Category)
class Meta:
ordering = ['-created_at']
verbose_name_plural = "Posteeees"
# def post_status(self):
# return timezone.now() - self.updated_at <= 1
#Recursive realation, we can define the foreignkey itself to the model and this is called rrecursive realation
#

Categories