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.
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.
I am designing a Django 1.8 application in which I have the concept of trials and assessors. I will have six assessors, who will use the Django admin to log in and make assessments.
I want each trial to have two attached assessors. How can I use the Django User model in models.py to ensure that the assessors are Users, and can be managed using the full power of Users?
Right now I have this, in which the assessors are not Users, but are just ordinary models:
class Assessor(models.Model):
name = models.CharField(max_length=200)
class Trial(models.Model):
title = models.CharField(max_length=800)
publication_date = models.DateField()
first_assessor = models.ForeignKey(assessor)
second_assessor = models.ForeignKey(assessor)
I want the assessors to be Users, so that I can manage them in the usual way through the User tables, but I don't know how to make this change.
They probably don't need any custom fields on top of the standard User attributes.
(NB: I don't need full-on permissions management within the admin, it's OK for any assessor to be able to edit the trial.)
UPDATE: Apologies, this is rather hard to explain! I don't care about the Django front-end at all, only the admin. I want a user to be able to log into the admin, see all the trials on which they are a primary assessor, and edit those trials. I'm not sure if it's best to do this with the User model, or not.
Instead of two foreign keys add a manytomany relation from Trail to user. Later, if you want you can add more assessors to a trail.
You can do like:
from django.contrib.auth.models import User
class Trial(models.Model):
title = models.CharField(max_length=800)
publication_date = models.DateField()
assessors = models.ManyToManyField(User,related_name="trials")
You can add assessors to Trial like:
trial = Trial.objects.get(id=give-trial-id)
user = User.objects.get(id=give-assessor-id)
trail.assessors.add(user)
You can get more info about manytomany here
I just saw your UPDATE. Any one who logs in to admin will have access to all objects of all models.
In your case all assessors who login to admin will have access to all trails irrespective of they are assigned to it or not.
If you want Filter django admin by logged in user then refer this question
Hi All!
I have a model structured something like this
class BaseUser(models.Model):
user_data = models.ForeignKey(settings.AUTH_USER_MODEL) #External Auth User Model
class Teacher(BaseUser):
pass
class Student(BaseUser):
pass
And I am adding all the models to Django admin like so.
for model in get_models(get_app('MyApp')):
admin.site.register(model)
In the admin panel, I can create/view a list of BaseUser, Teacher, and Student. Where Teacher/Student are subsets of BaseUser.
The Question
When a new user is created, it is automatically a BaseUser.
Is there a way to change the class of an user from BaseUser to Teacher or Student in the admin panel?
No there's no builtin way to do it in admin. You either have to code it yourself or if you want to create Teacher do it from it's admin create view.
It's not only about python class of model but also database representation. For each model that use concrete inheritance, special table is created that holds additional fields for the model subclass and automatically created OneToOneField to parent. Details here.
Edit:
Try to specify parent link field which may be manageable from admin and so it'll allow you to create e.g. new Teacher linked with already existing BaseUser.
Edit:
Specifying parent link will not help as well because that field will not appear in admin.
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
In Django you have some naturally defined User class. My app also has a User class defined (they dont conflict, that's not the question)
My question is, since these two User classes conceptually represent the same thing (well, users) then it would be natural to integrate them. That is, have a single User class that contains all methods and variables of both classes.
What is the best way to achieve this?
There are (at least) two possibilities:
1) Use the 'custom user' functionality of Django (since Django 1.5), or
2) Use a OneToOneField to the django.contrib.auth User from your own user class.
The first allows you to customize more, but you might get some problems if you try to use third-party-apps that are either not ready for custom users or need specific properties of the stock User. For example, Django Guardian doesn't work if you remove the User-Group relationship.
The second is less intrusive, but doesn't allow you to customize the existing fields of User. Also, you need to manually create the instance of your own user class at registration time.
You should read the documentation about Extending the existing User model.
If you wish to store information related to User, you can use a one-to-one relationship 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. For example you might create an Employee (note: called MyUser below) model:
from django.contrib.auth.models import User
class MyUser(models.Model):
user = models.OneToOneField(User)
newfield1 = models.CharField(...)
AUTH_USER_MODEL = 'myapp.MyUser'