I had to show many to many fields on admin pages of both models that had a many to many relationship and I used this approach which worked perfectly.
I had to show a field of tags while adding/editing user. The field was added, but I lost the functionality of password hashing. So now, I've to use UserAdmin as suggested here
Actually, I'm trying this:
class CustomUserAdmin(UserAdmin):
form = UserForm
which is not working with the existing form i.e. the tags field is not displayed at all. What I need to change?
Try to create inlines. Inlines let you show and customize related object in admin panel.
Related
Is it possible to create and delete new charfields or textareas through the Django admin page without harcoding them?
For example, I have a simple model, registered in Django admin page
class DocumentList(models.Model):
title = models.CharField(max_length=200)
def __str__(self):
return self.title
Obviously, it has only one charfield on admin page, something like:
DocumentList: [___________]
How can I add another one and delete her later if needed from Django admin page without actually hardcoding another charfield/textarea in models.py, to make it look like:
DocumentList: [___________]
*****************[___________]
Django models are not meant to be dynamically altered. You have to explicitly add the fields on your model, run migrations to have the fields created in your database backend, and reload your server process (./manage.py runserver does this automatically).
If you want to create a model that can hold an arbitrary amount of text strings instead of just one or a fixed amount, you need to use a many-to-many relation to another model.
You can use a custom form in the admin, either by using the form option of the get_form method. This is the documentation example for how you'd pass a custom form:
from django import forms
from django.contrib import admin
from myapp.models import Person
class PersonForm(forms.ModelForm):
class Meta:
model = Person
exclude = ['name']
class PersonAdmin(admin.ModelAdmin):
exclude = ['age']
form = PersonForm
You can add extra fields, as in any form.
I was wondering why you wanted this. Since you said in a comment it is to submit information to an API, you can also use an action, taking input from the user in an intermediate page.
EDIT: As became apparent in comments, the form needs to be dynamic for the user, and not when it is created. Therefore, the solution is using inlines, which once created and linked to the current model, allow the user to add any number of related forms to the current form.
The answer to question Django admin ManyToMany inline "has no ForeignKey to" error refers to the Django Admin documentation. The models given there are:
class Person(models.Model):
name = models.CharField(max_length=128)
class Group(models.Model):
name = models.CharField(max_length=128)
members = models.ManyToManyField(Person, related_name='groups')
and the inline admin classes are:
class MembershipInline(admin.TabularInline):
model = Group.members.through
class PersonAdmin(admin.ModelAdmin):
inlines = [MembershipInline,]
class GroupAdmin(admin.ModelAdmin):
inlines = [MembershipInline,]
exclude = ('members',)
... which allows group membership to be managed from the Person page but not from the Group page. But what if the administrator wants to manage members only from the Group page? Getting rid of the exclude line would allow both pages to manage the relationship, but the Django documentation (probably incorrectly) says "you must tell Django’s admin to not display this widget". What they probably mean is that you "should" tell Django's admin not to display it - nothing bad will happen if you don't, but it's redundant.
So without changing the models, is it possible to exclude the membership widget from the Person page instead of from the Group page? Both obvious attempts:
class PersonAdmin(admin.ModelAdmin):
inlines = [MembershipInline,]
exclude = ('Group.members',)
and
class PersonAdmin(admin.ModelAdmin):
inlines = [MembershipInline,]
exclude = ('groups',)
(the second using the related_name from the model) fail with the error:
'PersonAdmin.exclude' refers to field 'groups' that is missing from the form.
Yes, the model could be changed to put the ManyToManyField under Person. But since it is a symmetric relationship, there is no logical reason why it could not be managed from either Person or Group (but not both) without having to change the database schema. Can Django Admin manage group membership from the group page and exclude it from the person page?
what if the administrator wants to manage members only from the Group
page?
#admin.register(Person)
class PersonAdmin(admin.ModelAdmin):
pass
#admin.register(Group)
class GroupAdmin(admin.ModelAdmin):
pass
Django by default shows a Person m2m widget in the GroupAdmin. You correctly use the through model to get inlines, but the inlines are a separate definition not affected by the exclude. EDIT: Another simple way to put it is that you only specify the inlines on the Admin where you want them, no need to specify them on the opposite side's Admin.
Using inlines:
from core.models import Group, Person
class MembershipInline(admin.TabularInline):
model = Group.members.through
#admin.register(Person)
class PersonAdmin(admin.ModelAdmin):
pass
#admin.register(Group)
class GroupAdmin(admin.ModelAdmin):
inlines = [MembershipInline, ]
exclude = ('members',)
(tested on Django 3.0.3)
You don't give a reference for this claim:
the Django documentation (probably incorrectly) says "you must tell Django’s admin to not display this widget".
so I can only refer to the current (1.10) documentation for Django. It currently says of ManyToMany fields in the admin:
Django displays an admin widget for a many-to-many field on the model that defines the relation (in this case, Group). If you want to use an inline model to represent the many-to-many relationship, you must tell Django’s admin to not display this widget - otherwise you will end up with two widgets on your admin page for managing the relation.
So, in response to your correct statement:
But since it is a symmetric relationship, there is no logical reason why it could not be managed from either Person or Group (but not both) without having to change the database schema.
the reason is that the many-to-many relationship has to be defined somewhere; you have chosen to define it on the Group model, so that determines the default admin behaviour. If you want to move it, then you'll need to do a database migration to make that happen.
If, on the other hand, you want this documented behaviour to be different without changing your use of it — you don't seem to be asking a question that fits at StackOverflow. Better to report a bug with the program at the project's bug tracker, asking for a change in the software's behaviour.
I have a model with some default entries in a choices field. I was wondering if it was possible for the admin to add and remove entries to the choices from the admin site. The only other option I see at the moment is to have a separate table for the entries, but the spec specifically says to only use one table and I'm fairly new to Django. The current model code is very basic, and I haven't added any forms to admin.py, only registered my model. Example code:
class Contact(models.Model):
#some other fields here...
...
TYPES = (
('op1','option1'),
('op2','option2'),
('op3','option3')
)
option = models.CharField(
max_length=3,
choices=TYPES,
default='op1'
)
I want the super user to be able to click an add/remove type button on the admin page to open a new box which will allow them to edit the possible types.
Turns out I had to make a new model after all. it's fine, the admin site works as it needs to.
I have a many-to-many field between two models in Django. I however only see a form field in one of the models on the admin page. I tried adding a many-to-many field in the second model, and although this added a form field in the admin page, the two form fields were not synchronized (so changing the value on one form field doesn't affect the other one). Is there a way to have a many-to-many relationship and have two form fields in the admin page and both are synchronized?
There is an open source Django app called django-admin-extend which addresses the issue of bidirectional many-to-many fields using add_bidirectional_m2m and _get_bidirectional_m2m_fields. It can be installed via pip.
https://github.com/kux/django-admin-extend
https://pypi.python.org/pypi/django-admin-extend
If you define the m2m relationship in both models, and set the "through" attribute of one to be equal to the "through" of the other, you get access to the m2m relationship from both sides, and get to see it from both admin pages.
class Test1(models.Model):
tests2 = models.ManyToManyField('Test2', blank=True)
class Test2(models.Model):
tests1 = models.ManyToManyField('Test1', through=Test1.tests2.through, blank=True)
as seen in https://code.djangoproject.com/ticket/897
I'm attempting to combine the admin form for the User and my UserProfile model. I found out how to do it here, which works. I have this now:
#DJANGOPROJECT/generic/admin.py
class UserProfileInline(admin.TabularInline):
model = UserProfile
class UserAdmin(admin.ModelAdmin):
inlines = [UserProfileInline]
admin.site.unregister(admin.models.User)
admin.site.register(admin.models.User, UserAdmin)
However, the form generated in the admin contains five instances of the user profile form, instead of one. I don't understand why this is?
https://docs.djangoproject.com/en/dev/ref/contrib/admin/#django.contrib.admin.InlineModelAdmin
InlineModelAdmin.extra This controls the number of extra forms the
formset will display in addition to the initial forms. See the
formsets documentation for more information.
New in Django 1.2: Please, see the release notes For users with
JavaScript-enabled browsers, an "Add another" link is provided to
enable any number of additional inlines to be added in addition to
those provided as a result of the extra argument.
The dynamic link will not appear if the number of currently displayed
forms exceeds max_num, or if the user does not have JavaScript
enabled.
InlineModelAdmin.max_num This controls the maximum number of forms
to show in the inline. This doesn't directly correlate to the number
of objects, but can if the value is small enough. See Limiting the
number of editable objects for more information.
Try to set max_num to 1