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.
Related
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.
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.
hello i'm new in python and django
I need a view that get current user profile I know I shoud use get_profile from User but I don't know how to use it . i read the django document and It didn't help me.
this is what I found from doc:
from django.contrib.auth.models import User
profile=request.user.get_profile()
Django's documentation says it all, specifically the part Storing additional information about users. First you need to define a model somewhere in your models.py with fields for the additional information of the user:
models.py
from django.contrib.auth.models import User
class UserProfile(models.Model):
# This field is required.
user = models.OneToOneField(User)
# Other fields here
accepted_eula = models.BooleanField()
favorite_animal = models.CharField(max_length=20, default="Dragons.")
Then, you need to indicate that this model (UserProfile) is the user profile by setting AUTH_PROFILE_MODULE inside your settings.py:
settings.py
...
AUTH_PROFILE_MODULE = 'accounts.UserProfile'
...
You need to replace accounts with the name of your app. Finally, you want to create a profile every time a User instance is created by registering a post_save handler, this way every time you create a user Django will create his profile too:
models.py
from django.contrib.auth.models import User
class UserProfile(models.Model):
# This field is required.
user = models.OneToOneField(User)
# Other fields here
accepted_eula = models.BooleanField()
favorite_animal = models.CharField(max_length=20, default="Dragons.")
def create_user_profile(sender, instance, created, **kwargs):
if created:
UserProfile.objects.create(user=instance)
post_save.connect(create_user_profile, sender=User)
Accessing the Profile
To access the current user's profile in your view, just use the User instance provided by the request, and call get_profile on it:
def your_view(request):
profile = request.user.get_profile()
...
# Your code
Basically django User models will provide access only for the fields ( firstname,lastname,email,password,is_staff,is_active,last_login).
However if we want to add any extra fields to this model, say we need to add a new column named dateofbirth for every user, then we need to add a column named DOB into User model. But this is not possible as we aren't able to edit django User models.
To achieve this either
1.We can have a separate new table with email id & DOB column, such that a column in User model is mapped with a column in the new table. But this will create a new db instance for every db request. Say if u want to find the DOB of a customer,
First we need to fetch the value of mapped id of a customer from the
User table.
WIth the above value, get DOB from the new table.
In the second method,
Instead of using django User model, use your own customize model with all the fields needed. However if any updation related to security or some enhancement made to django User model we can't use it directly. We need to do more code changes at our end( wherever we use our customize models.) This will be a bit pain for a developer to identify the code & make changes.
To overcome the above issues, django introduce django profile which is very simple and more flexible. The advantages are
Updation/enhancement to the User model can be applied without modifying the code much
No need of creating new db instance to fetch the extra values.
Since the field has onetoone mapping deletion of data from one table will delete others also.
More secure, since we use django models ( no sql injection)
How to Use this:
In settings.py create a variable AUTH_PROFILE_MODULE = "appname.profiletable"
In models.py, create a new table with the fields needed and make sure that the id in User model is onetoone mapped with new table.
create a signal which inserts a row into the new table whenever a new entry is added into User model.
The value in the new table can be accessed using User object itself.
Say, we created a new table extrauser which has DOB, emailid. To find the DOB of a customer, use
a=User.objects.get(email='x#x.xom')
a.get_profile().DOB will give the dateofbirth value from extrauser table.
Hope the above details make you clear in understanding django profile. Incase of any help further, let me know. I have used django profile in my project.
Old question but I thought anyone seeing it today may benefit from this:
Django 1.5 adds the ability to - easily - extend the User model. This may be preferable as you now only got one object to deal with rather than two! Seems the more modern way.
https://hurricanelabs.com/blog/django-user-models/
You need to specify which class is your "Profile" by setting AUTH_PROFILE_MODULE = 'accounts.UserProfile' (for example)
https://docs.djangoproject.com/en/1.4/topics/auth/
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.