I'm making a user login form with a CustomUser model derived from AbstractUser, with one extra field: date_of_birth. I use CreateView to generate the form. All fields show up, the password field uses the password widget as expected (showing dots instead of characters), but the date field does not (plain character field with no formatting or calendar). What am I overlooking?
models.py:
from django.urls import reverse
from django.contrib.auth.models import AbstractUser
# Create your models here.
class CustomUser(AbstractUser):
date_of_birth = models.DateField(verbose_name="Date of Birth", blank=True, null=True)
def get_absolute_url(self):
return reverse('index')
def __str__(self):
return self.username
forms.py:
from .models import CustomUser
class CustomUserForm(forms.ModelForm):
class Meta:
model = CustomUser
fields = ["username", "password", "first_name", "last_name", "email", "date_of_birth"]
widgets = {
"password": forms.PasswordInput(),
"date_of_birth": forms.DateInput()
}
views.py:
from django.views.generic.edit import CreateView
from .models import CustomUser
from .forms import CustomUserForm
# Create your views here.
def index(request):
return HttpResponse("Hello, world")
class CustomUserCreate(CreateView):
model = CustomUser
form_class = CustomUserForm
If you come here in 2020 and beyond, just overide the default type=text undelying input by using 'type':'date'
So something like the below would work. Tested on Mozilla Dev Edition 73.+
'date_of_birth': forms.DateInput(attrs={'class':'form-control', 'type':'date'}),
Django has no built-in fancy datepicker. DateField uses the DateInput widget which is just a text input.
Thanks voodoo-burger for pointing me in the right direction. I found a video with a very simple solution to use the HTML5 datepicker: https://www.youtube.com/watch?v=I2-JYxnSiB0.
It only requires to add the following to forms.py:
class DateInput(forms.DateInput):
input_type = 'date'
and then use this as the widget (so replace forms.DateInput() with DateInput()).
Related
I'm using django's built-in User model, but I also have my own Account model which extends it:
class Account(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
age = models.IntegerField(blank=True)
location = models.CharField(max_length=255, blank=True)
experience = models.TextField(blank=True)
in my admin.py file:
class AccountInline(admin.StackedInline):
model = Account
can_delete = False
verbose_name_plural = 'Accounts'
class CustomUserAdmin(UserAdmin):
inlines = (AccountInline,)
admin.site.unregister(User)
admin.site.register(User, CustomUserAdmin)
I want it to be that when a User registers they enter this information in, but my issue is getting both the UserForm and AccountForm in the same template/view.
As of now this is my registration view:
class UserRegistration(generic.CreateView):
form_class = RegisterForm
template_name = 'registration/registration.html'
def form_valid(self, form):
user = form.save()
form.registration_notification()
login(self.request, user, backend='django.contrib.auth.backends.ModelBackend')
return redirect(self.request.GET.get('next'))
How do I add my AccountForm to this view as well so that I can render both in the template and submit with one button. I've seen people do it with a function based view but is there a way to do it with a class-based view?
I also want the same idea for my UpdateView where a User can update User information, but also Account information. I assume it would follow the same logic.
You can use a Custom user model "Extend AbstractUser" since you want to add extra fields and add as many other fields as you want in a single model and avoid making extra queries to the database.
From Django documentation :
from django.contrib.auth.models import AbstractUser
class User(AbstractUser):
field 1
field 2
forms.py
from django.contrib.auth.forms import UserCreationForm
from myapp.models import User
class CustomUserCreationForm(UserCreationForm):
class Meta(UserCreationForm.Meta):
model = User
fields = UserCreationForm.Meta.fields + ('custom_field',)
You can read more here
forms.py
from django import forms
from django.contrib.auth.models import User
from django.contrib.auth.forms import UserCreationForm
from .models import Profile
class UserRegisterForm(UserCreationForm):
email = forms.EmailField()
registration = forms.IntegerField(label='Registration Number')
class Meta:
model = User
fields = ['username', 'email','registration','password1', 'password2']
How can I remove the up and down arrows from the registration field?
P.S. I want to only allow the user to input numbers in this field.
This can be done in CSS:
Like so:
input::-webkit-outer-spin-button,
input::-webkit-inner-spin-button {
-webkit-appearance: none;
margin: 0;
You should be able to make it specific to that field by using:
#id_registration::-webkit-outer-spin-button,
#id_registration::-webkit-inner-spin-button {
-webkit-appearance: none;
margin: 0;
You can do this by overriding widgets in Meta class.
Like this:
from django import forms
from django.contrib.auth.models import User
from django.contrib.auth.forms import UserCreationForm
from .models import Profile
# Import widget that you want
from django.forms import TextInput
class UserRegisterForm(UserCreationForm):
email = forms.EmailField()
registration = forms.IntegerField(label='Registration Number')
class Meta:
model = User
fields = ['username', 'email','registration','password1', 'password2']
widgets = {
'registration': TextInput()
}
Also I'm not sure but you can just try this:
class UserRegisterForm(UserCreationForm):
email = forms.EmailField()
# Add widget to your field
registration = forms.IntegerField(label='Registration Number', widget=forms.TextInput())
class Meta:
model = User
fields = ['username', 'email','registration','password1', 'password2']
For more info:
Overriding the default fields
I'm using UserCreationForm in Django and I'm trying add help_text to the email field.
Here's the forms.py code:
from django.contrib.auth import get_user_model
from django.contrib.auth.forms import UserCreationForm, UserChangeForm
class CustomUserCreationForm(UserCreationForm):
class Meta(UserCreationForm):
model = get_user_model()
fields = ('email','username',)
help_texts = {
'email': 'Use your work email',
}
I'm using Crispy Forms to render it and it isn't rendering the help_text.
As another approach, I tried adding this to the class:
def __init__(self, *args, **kwargs):
super(CustomUserCreationForm, self).__init__(*args, **kwargs)
self.fields['email'].help_text = 'hello'
But it didn't work either.
What am I missing?
EDIT: I realised I made a mistake with this question. This project had switched to using django-allauth, but left forms.py in the user app of this django project. Therefore none of the changes in these forms were having any effect. A check of the settings would've shown that it was using allauth.
help_texts is not an attribute of the Meta class of a form. You can add the help text for the email field in your model, like this:
email = models.EmailField(max_length=200, help_text='use your work email', blank=True, null=True)
Since email is not one of the fields of UserCreationForm (see https://docs.djangoproject.com/en/3.0/topics/auth/default/#django.contrib.auth.forms.UserCreationForm), setting the field via init will not work.
I am a Django newbie working with Django CBVs and having difficulty setting initial values for my ModelForm. To give an overview, I am trying to learn by creating a simple messaging app.
Here is my code:
models.py
import datetime
from django.contrib.auth.models import User
from django.db import models
from django.utils.translation import ugettext_lazy as _
from django.utils import timezone
class Message(models.Model):
subject = models.CharField(_("Subject"), max_length=100)
body = models.TextField(_("Body"))
sender = models.ForeignKey(User, db_index=True, related_name='sent_messages')
recipient = models.ForeignKey(User, db_index=True, related_name='received_messages')
parent_msg = models.ForeignKey('self', related_name='next_messages', null=True, blank=True)
forms.py
from django.forms import ModelForm
from .models import Message
class MessageForm(ModelForm):
class Meta:
model = Message
exclude = ('sender', 'recipient', 'parent_msg',)
views.py
class MessageCreateView(CreateView):
form_class = MessageForm
model = Message
template_name = 'messages/compose.html'
def form_valid(self, form):
form.instance.sender = self.request.user
return super(MessageCreateView, self).form_valid(form)
urls.py
...
url(r'^compose/(?P<recipient>[\w.#+-]+)/$', MessageCreateView.as_view(), name='messages_compose_to'),
...
As you can see from the urls.py file, I am using the 'recipient' parameter as such: http://localhost:8000/members/compose/someusername
Now my problem is that I wish to open the compose message view, and initialize the recipient field by getting the username from the URL, then using the username from the url to get User with that particular username, and instantiate the form with it.
Where do I do this, in the view itself or in the form? Unless their is a better way of how to handle this.
You can add get_initial() method to return appropriate dict, something as below.
class MessageCreateView(CreateView):
...
def get_initial(self):
data = { 'recipient':
User.objects.get(username=self.kwargs.get('recipient'))
}
return data
Handle error appropriately.
I have a new project on django, in which im using Grappelli and filebrowser, and I have extended the User to have a UserProfile related to it, my question is, how can I modify my code to be able to show on the UserProfile information of a user the profile picture uploaded, and also show it on the Users list?
This is my code now, I dont see any image on the admin!
Admin.py
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from django.contrib.auth.models import User
from models import UserProfile
class UserProfileInline(admin.StackedInline):
model = UserProfile
verbose_name_plural = 'User Profile'
list_display = ('city', 'tel', 'description', 'image_thumbnail',)
class MyUserAdmin(UserAdmin):
list_display = ('username','email','first_name','last_name','date_joined',
'last_login','is_staff', 'is_active',)
inlines = [ UserProfileInline ]
admin.site.unregister(User)
admin.site.register(User, MyUserAdmin)
Models.py
from django.db import models
from django.contrib.auth.models import User
from django.db.models.signals import post_save
from django.utils.translation import ugettext_lazy as _
from apps.common.utils.abstract_models import BaseModel
from apps.common.utils.model_utils import unique_slugify
from filebrowser.base import FileObject
from django.conf import settings
class UserProfile(BaseModel):
user = models.OneToOneField(User, related_name="profile")
city = models.CharField(_("City"), max_length=200)
tel = models.CharField(_("Phone Number"), max_length=50,
help_text=_("(Area Code) (Your phone number)"))
description = models.TextField(null=True, blank=True,
help_text = _("Small description about yourself."))
photo = models.ImageField(max_length=255, upload_to="profiles/",
null=True, blank=True, default="img/default_profile_image.png")
def image_thumbnail(self):
if self.photo:
return u'<img src="%s" width="80" height="80" />' % self.photo.version(ADMIN_THUMBNAIL).url
return u'<img src="/site_media/%s" width="80" height="80" />' % settings.DEFAULT_PROFILE_IMAGE
image_thumbnail.allow_tags = True
def __unicode__(self):
if self.user.first_name or self.user.last_name:
return "%s %s" % (self.user.first_name, self.user.last_name)
else:
return self.user.username
Well I got it, first I wanted to show the image chosen on the UserProfile inline section of the user model for the admin and also on the change list of the admin so heres what I
I changed the models.ImageField to sorl ImageField on the model.py of User profile like this
from sorl.thumbnail import ImageField
class UserProfile(BaseModel):
[...]
photo = ImageField(max_length=255, upload_to="profiles/",
null=True, blank=True, default="img/default_profile_image.png")
Then on the admin all I had to do was add sorl's AdminImageMixin on the UserProfileInline class, like this:
from sorl.thumbnail.admin import AdminImageMixin
class UserProfileInline(AdminImageMixin, admin.StackedInline):
model = UserProfile
verbose_name_plural = 'User Profile'
And that way you get an image on the UserProfile Inline section on the admin for that user, now for the change_list.
For the change list I had to do a small callable function inside the admin.py file on the UserAdmin class, heres what I did, using sorl's get_thumbnail:
from sorl.thumbnail import get_thumbnail
class MyUserAdmin(UserAdmin):
def image_thumbnail(self, obj):
im = get_thumbnail(obj.get_profile().photo, '80x80', quality=99)
return u"<img src='/site_media/%s' />" % im
image_thumbnail.allow_tags = True
list_display = ('image_thumbnail', 'username','email','first_name','last_name','date_joined',
'last_login','is_staff', 'is_active',)
And now I have a change list image of the user profile and also on the UserProfile Inline section.
Hope this works for everyone out there... and thanks #pastylegs for your previous answer!
list_display needs to be a callable:
https://docs.djangoproject.com/en/dev/ref/contrib/admin/#django.contrib.admin.ModelAdmin.list_display
so you can do:
class UserProfileInline(admin.StackedInline):
def image_thumbnail(self, obj):
return obj.image_thumbnail()
image_thumbnail.short_description = 'Thumbnail'
model = UserProfile
verbose_name_plural = 'User Profile'
list_display = ('city', 'tel', 'description', 'image_thumbnail',)