I was building a login/logout User system with django, and I started playing around with the django auth system.
I am using the custom Django UserCreationForm like so:
views.py
from django.contrib.auth.forms import UserCreationForm
def register(request):
if request.method == 'POST':
form = UserCreationForm(request.POST)
if form.is_valid():
new_user = form.save()
return HttpResponseRedirect("/books/")
else:
form = UserCreationForm()
return render(request, "registration/register.html", {
'form': form,
})
But this form renders a lot of unwanted stuff, as shown here: https://app.box.com/s/wmrtyal3mctb9hctsnom
First, I would like to get rid of the added information, is there a good way to do this? Are there any good ways to do this? Is there a way to edit the UserCreationForm myself or would I have to create my own form?
Second, lets assume I want to add more required fields to a registration page, what is the best way to do this? Say I wanted to make it the case that a person register male or female -I realize I should extend the user model, but how would I register both to the original user model and the extended user model?
Rather than extend the UserCreationForm class, you should rewrite it. Check out the source for it here.
For example, if you wanted to get rid of the text next to the password field that says 'Enter the same password as above', remove this line:
help_text=_("Enter the same password as above, for verification."))
from django /django/contrib/auth/forms.py (in the link above)
EDIT: The reason I suggested that you rewrite the forms classes rather than extending them is because it is simply more convenient this way. From the Django docs:
If you don’t want to use the built-in views, but want the convenience
of not having to write forms for this functionality, the
authentication system provides several built-in forms located in
django.contrib.auth.forms
If you were to extend your User model classes you would need to change the form classes accordingly. Again from the docs:
As you may expect, built-in Django’s forms and views make certain
assumptions about the user model that they are working with. If your
user model doesn’t follow the same assumptions, it may be necessary to
define a replacement form, and pass that form in as part of the
configuration of the auth views.
See here and here for more details
If you simply want to change the UserCreationForm you would edit the UserCreationForm class as in the forms.py module as I mentioned above.
If, as you mentioned in your question, you want to add more required fields to a registration page, you would need to make a custom User model. See here for details on how to do this
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 have a form that allow users to log their activities. To make it simple, let's say I only have two fields that I want a user to fill out.
Time
Action
During a day, a user can fill out multiple time + action pairs. I used javascript on the front end to allow users to add these pairs as they wish.
Thus, I do not know how many pairs there will be beforehand. And thus, I cannot create a predefined ModelForm for it.
To deal with this issue, I labeled each Time and Action field with a unique name. So when I receive a POST request, I geta list like this inside the request.POST dictionary:
time_1: 9:50
action_1: wakeup
time_2: 11:00
aciton_2: workout
...
Then, I subtract each pair out of the dictionary and put them into a ModelForm for validation and save to the database.
class TimeActionModel(Model):
time = DateField()
action = CharField(max_length=100)
class TimeActionForm(ModelForm):
class Meta:
model = TimeActionModel
class TimeActionView(View):
def post(self, request, *args, **kwargs):
self._subtract_and_save(request)
def _subtract_and_save(request):
#loop through the request.POST dictionary
#pull out each pair
#stuff each one into a ModelForm object
if form.is_valid():
form.save()
Here is my quesiton:
Does this approach look right to you?
What's the 'Django way' of dealing with such situation?
Thank you!
There is a concept in Django called formset:
A formset is a layer of abstraction to work with multiple forms on the same page. It can be best compared to a data grid.
The Django way would be to use Model formsets:
Like regular formsets, Django provides a couple of enhanced formset classes that make it easy to work with Django models.
Therefore you could create a model formset for your TimeActionModel as such:
from django.forms.models import modelformset_factory
TimeActionFormset = modelformset_factory(TimeActionModel)
You can read more on that in the documentation. It has extensive use cases and examples to cover your case.
UPDATE: The extras parameter of the formset is not quite important. You can easily manipulate the number of extra forms in your formset with a bit of javascript. There are also contrib packages for that such as django-dynamic-formset.
UPDATE2: The name of the fields depends on the prefix used too, which I recommend it in case of many different forms/formsets in a single page, but you can easily deduce it looking at a default form that Django renders.
Also please take not not to forget in your template to include {{ my_formset.management_form }} and {{ my_formsets_form.id }}!
I'm really confused how I can change the registration_form to ask the users about their first and last name.
I think I need to manipulate subclass RegistrationView from registration.backends.simple.views but I don't know how to do this to show first and last name in registration form.
You should be able to sub-class RegistrationForm and add the fields you want.
Look at the source for the default forms on how to do so:
https://github.com/macropin/django-registration/blob/master/registration/forms.py
I'm using Flask with Flask-WTForms and am writing an admin page where it's possible to update values for a user - including the password.
I'm using the same form page that I use for registration, but since it's not requisite that the password be updated, I don't want to require it. What is the right way to do this with Flask-WTForms?
I've got my UserForm in forms.py and I was thinking of making a custom validator and have a file-level require_password option that would override the default check. I'm fairly new to WTForms, and somewhat new to Flask.
This solution seems to do what I want:
In forms.py,
from flask.ext.wtf import Optional
def make_optional(field):
field.validators.insert(0, Optional())
#Rest of code here...
Then inside my flask endpoint I can call:
user_form = UserForm()
forms.make_optional(user_form.password)
if user_form.validate_on_submit():
#Go on your merry way!
That seems to do what I wanted - keep all the other validation (e.g. confirmation), while ignoring anything if there is no password present.
I have a model named Domain which looks like this:
class Domain(models.Model):
"""
Model for storing the company domains
"""
user = models.ForeignKey(
User
)
host = models.CharField(
null=False, verbose_name="Host", max_length=128, unique=True
)
I'd like to use Django's generic views for doing CRUD operations on this. There is one field in this model that needs user input but the foreign key field doesn't need any user input. How can I exclude that field from the form that my generic view generates but assign it the value of the current authenticated user.
Thanks.
Have a look at Russel's answer to a similar question on the django-users group earlier this week.
Quoting the answer*:
Forms and Views solve different problems.
The View is solving the problem of "how do I handle this request and
convert it into a response?". The Form is solving the problem of "How
do I convert the POST data in this request into a model object (or a
change to a model object)?".
Very roughly, a view is doing the following:
View gets a request
View works out whether this is a GET or a POST
If its a POST, View asks the Form to turn the Post into a model change
Form returns success or failure
View responds to the success or failure of the Form.
View returns a response.
The functionality of the Form is a complete subset of the
functionality of the View -- and for this reason, it's a completely
interchangable internal component.
Now, in simple situations, it's possible for a View to guess all the
defaults for the form -- all it needs to know is that you're dealing
with a Foo model, and it can construct a default Foo ModelForm.
However, if you have more sophisticated form requirements, you're
going to need a customized Form.
We could have implemented this by exposing all the options of
ModelForm on the View class; but in order to keep everything clean, we
kept the ModelForm isolated, and provided the View with a way to
specify which Form class it's going to use.
So - to cover your use case of excluding fields, you define a
ModelForm that excludes the fields, then let the CreateView know the
form you want to use:
class CampaignForm(forms.ModelForm):
class Meta:
model = Campaign
exclude = ('user', 'name', 'content_inlined')
class CreateCampaignView(CreateView):
form_class = CampaignForm
template_name = "forms/create.html"
I'm guessing when you say "fix a values for a field", you mean setting
the values of user, name and content_inlined before you save the new
Campaign instance; to do this, you need to inject some extra code into
the form processing logic of the form:
class CreateCampaignView(CreateView):
form_class = CampaignForm
template_name = "forms/create.html"
def form_valid(self, form):
form.instance.user = ... (something meaningful.. e.g., self.request.user)
return super(CreateCampaignView, self).form_valid(form)
This overrides the default behavior when the form is valid, and sets
the extra values. The super() implementation of form_valid() will then
save the instance.
For the record, this could also be done by overriding the save()
method on the ModelForm -- however, if you do that, you lose the
request object, which you will need if you're trying to set the
instance values to something that is request-sensitive.
*the original answer set self.object.user instead of form.instance.user. This gives an AttributeError so I have changed it above.