Submit django model form in mutiple pages - django

I have a website where Django forms are used, I want to make my lengthy form into multiple parts. For example part A for personal information, then on clicking next part B of form like Professional information. Is there any way I can split my form into multiple parts?
also I'm using Django templates.

Yes, and thankfully with Django is very easy to do :
from django.contrib import admin
class FlatPageAdmin(admin.ModelAdmin):
fieldsets = (
(None, {
'fields': ('url', 'title', 'content', 'sites')
}),
('Advanced options', {
'classes': ('collapse',),
'fields': ('registration_required', 'template_name'),
}),
)
https://docs.djangoproject.com/en/4.1/ref/contrib/admin/#django.contrib.admin.ModelAdmin.fieldsets
Result being :

Related

Getting all registered users from Django's inbuilt user model into a class within admin.py

I am fairly new to Django and can just about articulate what I am trying to do, so I apologise if the title isn't accurate.
I am using this survey app - https://github.com/Pierre-Sassoulas/django-survey
Within admin.py, I have the following two classes:
class SurveyAdmin(admin.ModelAdmin):
list_display = ('name', 'is_published', 'need_logged_user', 'template')
list_filter = ('is_published', 'need_logged_user')
inlines = [CategoryInline, QuestionInline]
actions = [make_published]
class ResponseAdmin(admin.ModelAdmin):
list_display = ('interview_uuid', 'survey', 'created', 'user')
list_filter = ('survey', 'created')
date_hierarchy = 'created'
inlines = [
AnswerTextInline, AnswerRadioInline, AnswerSelectInline,
AnswerSelectMultipleInline, AnswerIntegerInline
]
# specifies the order as well as which fields to act on
readonly_fields = (
'survey', 'created', 'updated', 'interview_uuid', 'user'
)
These create the pages for "Responses" and "Surveys" in the Admin page.
Admin area with Response and Survey links / their outputs
I can list the usernames of each user that submitted a Response to the Survey (using TabularInLine and model.response), but what I want to do is list all of the users that exist (so that I can add a timestamp of when they submitted their Response to the Survey/Filter by Response date/etc.).
What I've tried since is importing User from contrib.auth.models and creating a new UserAdmin class (so that I can at least list all users, and move on from there).
from django.contrib.auth.models import User
...
admin.site.unregister(User)
...
class UserAdmin(admin.ModelAdmin):
list_display = ('username',)
...
admin.site.register(User, UserAdmin)
I don't get any errors, but it's not displaying the UserAdmin class in the Admin page.
How can I list all of the registered users into the Admin page (in order to later add the timestamp of when a Response was submitted, if one was submitted)?
I'm using Django 1.11 if that's relevant.
I think you should have a list of all the user in the admin with the code you gave. Maybe you don't have any users in database ? I can't see the exact problem right now.
But without modifying the app, you should be able to see when a "Response" has been submitted or updated with the created or updated attribute of the Response class :
For example if you want to list the user and date of submission of a survey contained in the survey variable:
for response in survey.reponses:
print(response.user, "answered at", response.created)
If you want to loop on all users and see if the survey has been answered:
for user in User.objects.all():
response = Reponse.objects.filter(survey=survey, user=user)
if response:
print(user, "answered at", response.created)
else:
print(user, "never answered")
I hope this help.

How to add fields to Django User model and show it on registration form with django-registration?

I noticed that the User model that Django provides does not have a Date of Birth Field.
I do not want to write my own custom User class just to include this attribute. I am wondering if it is possible in some way to "attach" date of birth to the User model. If not what are the other simple options to make this work. I am also ok to save the contents of User Model to a profile model with additional date of birth information.
But would my form has to save both User and profile model which I am confused as how this work.
EDIT 2: (After implementing frnhr's solution):
Two issues: No activation email is sent out in the development server. I tried default User model with django-registration and I see activation email being sent out. but now, with the custom user model, no activation email is sent out.
when I login as admin, http://127.0.0.1:8000/admin/, I see no User model but only registration profiles and Groups. when I click registration profiles, it does not show all the information about User, but just username and activation key which is shown as already expired. Attached is a screenshot. how to fix this?
In newer versions of Django, making your own custom User model is quite painless. See this answer for details: https://stackoverflow.com/a/16125609/236195
You might run into problems if you are using some older third-party apps which have model relationships hardcoded to auth.models.User. Most of third-party apps which have ongoing development have probably switched already or will do so soon.
Edit - getting it to work with django-registration
myapp/models.py:
from django.contrib.auth.models import AbstractUser
from django.db import models
from django.utils.translation import ugettext_lazy as _
class CustomUser(AbstractUser):
age = models.PositiveIntegerField(_("age"), null=True) # this field is added to default User model
settings.py:
INSTALLED_APPS = (
# ...
'registration_defaults',
'registration',
'myapp',
)
# ...
AUTH_USER_MODEL = "myapp.CustomUser"
ACCOUNT_ACTIVATION_DAYS = 2 # or something
urls.py (add this line):
url(r'^accounts/', include('registration.backends.default.urls')),
myapp/admin.py (optionally):
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from django.utils.translation import ugettext_lazy as _
from .models import CustomUser
class CustomUserAdmin(UserAdmin):
fieldsets = (
(None, {'fields': ('username', 'password')}),
(_('Personal info'), {'fields': ('first_name', 'last_name', 'email', 'age')}), # added "age" field
(_('Permissions'), {'fields': ('is_active', 'is_staff', 'is_superuser',
'groups', 'user_permissions')}),
(_('Important dates'), {'fields': ('last_login', 'date_joined')}),
)
admin.site.register(CustomUser, CustomUserAdmin)
django-registration
Get it from here: https://bitbucket.org/fhrzenjak/django-registration and put it in your project folder.
Disclaimer: this is my fork of the original django-registration repo, with applied patch for custom user models by mrginglymus: https://bitbucket.org/ubernostrum/django-registration/pull-request/30/django-15-compatibility-with-custom-user/diff
Django-registration-defaults is a collection of default templates from here: https://github.com/yourcelf/django-registration-defaults It's pip-installable
EDIT 2 - accept a custom field on registration
Well, this is a separate question, but ok here is how...
Create a MyRegistrationFormform:
from registration.forms import RegistrationForm
class MyRegistrationForm(RegistrationForm):
age = forms.CharField(required=False)
In your urls.py add a line before django-registration include. That will override whatever is in the include:
url(r'^accounts/register/$',
RegistrationView.as_view(form_class=MyRegistrationForm),
name='registration_register',
),
url(r'^accounts/', include('registration.backends.default.urls')),
Modify your custom user model to respond on user_registered signal and save the additional data:
class CustomUser(AbstractUser):
age = models.PositiveIntegerField(_("age"), null=True)
#classmethod
def user_created(cls, sender, user, request, **kwargs):
from myapp.forms import MyRegistrationForm
form = MyRegistrationForm(request.POST)
user.age = form.data.get('age', None)
user.save()
pass
from registration.signals import user_registered
user_registered.connect(CustomUser.user_created)
Your best bet is probably to go ahead and create a custom User model (you can subclass django.contrib.auth.models.AbstractUser and add whatever fields you want).
Regarding processing multiple models in single form, it's actually quite easy. You actually want to use multiple Forms, but render them both in the same <form> element, and process them separately in the view.
Another approach is just to create a single Form (probably not a ModelForm), which contains all of the fields you want. Then, write a save method on your form, which creates/updates the relevant models. This makes your view and template code simpler than the approach above.
As an example, here is a form class that lets an existing user change their email address and date of birth. Note that you pass the logged in User from your view when calling save():
class UserEditForm(forms.Form):
email = forms.EmailField()
date_of_birth = forms.DateField()
def save(self, user):
user.email = self.cleaned_data['email']
user.save()
user.userprofile.date_of_birth = self.cleaned_data['date_of_birth']
user.userprofile.save()
Note this assumes you have a UserProfile model, which has a one-to-one field to User.

Collapsible inlines in Django Admin

Is there an up-to-date option for allowing collapsible inlines in the Django admin site?
I've looked at https://code.djangoproject.com/ticket/494, How to add 'collapse' to a Django StackedInline and Grappelli, but none of them seem to work for Django 1.6.1.
Any suggestions?
clps = ('collapse', 'grp-closed')
or
css_classes=('collapse closed',)
These two ways seems to work for me with grappelli, You did not give code info to tell which is relevant,
POST your grappelli dashboard code(or anything else)
Adding this to your inline model (django-grappelli==2.6.5+) :
fieldsets = (
('yoour_description', {
'classes': ('grp-collapse', 'grp-closed'),
'fields' : ('field1', 'field2', ...),
}),
)

Django auth - Adding user fields - displaying in admin

I'm a complete n00b to django & python. I come from a PHP background so you'll have to accept my apologies for that :p.
I'm trying to use the admin panel functionality in django to show different options to different people.
The system should allow admins to add "projects" to a list. "Developers" should then be able to view only projects assigned to them, and only change certain fields.
So I guess the question is two fold:
1) Is allowing the "Developers" to login to the admin system the best method of doing it?
1.a) If so, How do I get a boolean field to display on the admin's user form? I just want to flag is_developer. I've added it as a userProfile but don't understand how to make it display on the form
2) Should I disallow them to login (to the admin panel) and make "frontend" whereby they can only see what they're allowed?
I hope that made sense. I'm a bit all over the place at the moment as it's a complete departure to what i'm used to!
Thanks in advance for any help you can offer me :)
There's a lot going on here, so I'm going to piecemeal my answer.
Is allowing the "Developers" to login to the admin system the best method of doing it?
That depends on your setup. Generally, the admin should only be available to "staff": people that are employed by or directly related to your organization. In fact, in order to login to the admin, a user must have is_staff=True. If all of the users belong to your organization (and can be considered "trusted" as a result), then yes, it's fine to allow them to all access the admin. Otherwise, it's not a good idea, as you're opening yourself up to security risks.
If so, How do I get a boolean field to display on the admin's user form?
In the most simplistic sense, you can add a field to a form by literally adding it to the form class, even if it's a ModelForm which pre-populates its fields from the fields on the model.
class MyModelForm(forms.ModelForm):
class Meta:
model = MyModel
is_developer = forms.BooleanField(default=False)
I've added it as a userProfile but don't understand how to make it display on the form
UserProfile is a different model, obviously, so its fields are not made available on a form for a User. However, Django does provide the ability to add/edit related models inline with edit form for another model. This is done through inline formsets. In the admin, these are just called "inlines".
class UserProfileInlineAdmin(admin.StackedInline):
model = UserProfile
max_num = 1
can_delete = False
class UserAdmin(admin.ModelAdmin):
inlines = [UserProfileInlineAdmin]
The view you get from an inline admin is clearly distinct from the main form (in this case, that of User), though. You can try it out to see what I mean. It's not horrible, but it's still a noticeable break in the form. The reason I mentioned how to add a field to a form earlier, is that if you wanted, you can make it look all like one form with a little bit of clever misdirection.
class UserAdminForm(forms.ModelForm):
class Meta:
model = User
is_developer = forms.BooleanField(default=False)
def save(self, commit=True):
user = super(UserAdminForm, self).save(commit=commit)
if user.pk:
profile = user.get_profile()
profile.is_developer = self.cleaned_data.get('is_developer')
profile.save()
That's a simplistic example, but the idea is that you add the field(s) manually to the form, and then use them to actually update the other object manually when the main object being edited is saved.
Special notes related to User
Now, since you're dealing with User here, there's a lot more sticky details. First, User already has a UserAdmin and its own forms -- yes plural, forms. If you want to add new functionality, you need to make sure you keep the existing Django functionality in the process.
from django.contrib.auth.admin import UserAdmin
form django.contrib.auth.models import User
from django.contrib.auth.forms import UserCreationForm, UserChangeForm
class CustomUserCreationForm(UserCreationForm):
# do stuff
class CustomUserChangeForm(UserChangeForm):
# do stuff
class CustomUserAdmin(UserAdmin):
form = CustomUserChangeForm
add_form = CustomUserCreationForm
admin.site.unregister(User)
admin.site.register(User, CustomUserAdmin)
Also, UserAdmin has its own set of fieldsets defined. The defaults are:
fieldsets = (
(None, {'fields': ('username', 'password')}),
(_('Personal info'), {'fields': ('first_name', 'last_name', 'email')}),
(_('Permissions'), {'fields': ('is_active', 'is_staff', 'is_superuser', 'user_permissions')}),
(_('Important dates'), {'fields': ('last_login', 'date_joined')}),
(_('Groups'), {'fields': ('groups',)}),
)
add_fieldsets = (
(None, {
'classes': ('wide',),
'fields': ('username', 'password1', 'password2')}
),
)
If you want to add a field or fields, you'll need to redefine those two attributes with your fields added where you want them.

Django admin - how to hide some fields in User edit?

How can I hide fields in admin User edit? Mainly I want to hide permissions and groups selecting in some exceptions, but exclude variable doesn't work :/
I may be late to answer this question but any ways, here goes. John is right in concept but I just wanted to do this because I know django admin is truly flexible.
Any way's the way you hide fields in User model form is:
1. exclude attribute of the ModelAdmin class can be used to hide the fields.
2: The should allow blank in model.
3: default attribute on model field is an advantage or you might get unexpected errors.
The problems I had was that I used to get a validation error. I looked at the trace back and found out that
the error was because of UserAdmin's fieldsets grouping, the default permission field set has user_permission override this in your sub-calassed model admin.
Use the exclude attribute in get_form where you can access request variable and you can set it dynamical depending on the user's permission or group.
Code:
admin.py:
class MyUserAdmin(UserAdmin):
list_display = ("username","first_name", "last_name", "email","is_active","is_staff","last_login","date_joined")
## Static overriding
fieldsets = (
(None, {'fields': ('username', 'password')}),
(_('Personal info'), {'fields': ('first_name', 'last_name', 'email')}),
(_('Permissions'), {'fields': ('is_active', 'is_staff', 'is_superuser',
'groups')}),
(_('Important dates'), {'fields': ('last_login', 'date_joined')}),
)
def get_form(self, request, obj=None, **kwargs):
self.exclude = ("user_permissions")
## Dynamically overriding
self.fieldsets[2][1]["fields"] = ('is_active', 'is_staff','is_superuser','groups')
form = super(MyUserAdmin,self).get_form(request, obj, **kwargs)
return form
The django admin is not designed for very fine grained control so their are no automated variables designed to allow this type of control.
If you need this type of control you're going to have to go it your own. You'll need to override the default admin templates. You'll probably want to use the permissions system to track what users are allowed to do.
Keep in mind the level of customization you're making. At some point working to far outside the intended purpose and limitations of the admin app will be more work than simply rolling your own more fine grained CRUD system.