Extend UserCreationForm for extended User in Django - django

I extended my django user and need to create a registration form now.
I got most of it figured out but I don't know how to exclude fields I don't need during registration. Right know I see all fields in the registration form.
Here is the code:
models.py
class Artist(Model):
user = OneToOneField(User, unique=True)
address = CharField(max_length=50)
city = CharField(max_length=30)
ustid = CharField(max_length=14)
date_of_birth = DateField()
bio = CharField(max_length=500)
def __unicode__(self):
return self.user.get_full_name()
User.profile = property(lambda u: Artist.objects.get_or_create(user=u)[0])
forms.py
class RegistrationForm(UserCreationForm):
class Meta:
model = User
def __init__(self, *args, **kwargs):
super(RegistrationForm, self).__init__(*args, **kwargs)
artist_kwargs = kwargs.copy()
if kwargs.has_key('instance'):
self.artist = kwargs['instance'].artist
artist_kwargs['instance'] = self.artist
self.artist_form = ArtistForm(*args, **artist_kwargs)
self.fields.update(self.artist_form.fields)
self.initial.update(self.artist_form.initial)
def clean(self):
cleaned_data = super(RegistrationForm, self).clean()
self.errors.update(self.artist_form.errors)
return cleaned_data
def save(self, commit=True):
self.artist_form.save(commit)
return super(RegistrationForm, self).save(commit)
How do I exclude fields?

class Meta:
model = User
exclude = ('bio',)

You can't include or exclude fields that are not a member of the meta model.
What you can do is doing that in each form. In this case the UserCreationForm is extended by the ArtistForm. Just restrict the fields in the form that belong to the right meta model.

Related

Django limit choice of user field foreign key based on the user that logged in

I have a model called Client with user field as a foreign key:
class Client(models.Model):
name = models.CharField(_('Client Name'), max_length=100)
address = models.CharField(_('Client Address'), max_length=100, blank=True)
demand = models.PositiveIntegerField(_('Client Demand'))
location = models.PointField(_('Client Location'))
created_at = models.DateTimeField(auto_now=True)
user = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,
)
class Meta:
default_permissions = ('add', 'change', 'delete', 'view')
def __str__(self):
return self.name
I want to limit the choice of the user field in the admin form based on who logged in
for example, here I logged in as agung, so I want the select box choice of user field limit only to agung, but here I can access other username like admin and rizky.
I tried this
class ClientAdminForm(forms.ModelForm):
class Meta:
model = Client
fields = "__all__"
def __init__(self, request, *args, **kwargs):
super(ClientAdminForm, self).__init__(request, *args, **kwargs)
if self.instance:
self.fields['user'].queryset = request.user
but it seems that it can't take request as an argument (I guess because this is not an Http request)
You can overwrite your Admin Model's get_form method to add the current request.user as class property. Next you can read it in the Form's constructor and filter the query.
class ClientAdmin(admin.ModelAdmin):
# [...]
def get_form(self, request, obj=None, **kwargs):
form_class = super(ClientAdmin, self).get_form(request, obj, **kwargs)
form_class.set_user(request.user)
return form_class
class ClientAdminForm(forms.ModelForm):
# [...]
#classmethod
def set_user(cls, user):
cls.__user = user
def __init__(self, *args, **kwargs):
super(ClientAdminForm, self).__init__(*args, **kwargs)
self.fields['user'].queryset = \
self.fields['user'].queryset.filter(pk=self.__user.pk)
However, is easiest exclude this field in form and update it in the save_model method:
class ClientAdmin(admin.ModelAdmin):
# [...]
def save_model(self, request, obj, form, change):
obj.user = request.user
obj.save()
You can do it by override the base_fields attribute of your form instance like this :
views.py
# Before instantiate the form class
ClientAdminForm.base_fields['user'] = forms.ModelChoiceField(queryset=self.request.user)
# Now you can instantiate the form
form = ClientAdminForm(...)
NB : Do override the base_fields just before instantiate the form

How to filter values in a Django form using ModelForm?

The problem is that I have a model secciones that is associated with a user and a productos model that is also associated with a user and model secciones,
class secciones(models.Model):
name = models.CharField(max_length=50)
user = models.ForeignKey(User)
def save(self, *args, **kwargs):
if not self.id:
self.slug = slugify(self.name)
super(secciones, self).save(*args, **kwargs)
def __unicode__(self):
return self.name
class productos(models.Model):
user = models.ForeignKey(User)
secciones = models.ForeignKey(secciones)
name = models.CharField(max_length=50)
image = models.ImageField(upload_to = 'productos')
precio = models.DecimalField(max_digits=10, decimal_places=2, default=0.00)
descripcion = models.TextField(max_length=300, null=True,blank=True)
def save(self, *args, **kwargs):
if not self.id:
self.slug = slugify(self.name)
super(productos, self).save(*args, **kwargs)
def __unicode__(self):
return self.name
I am creating a form so that the user can enter a new product in the productos model, but I just want to leave the sections of the model secciones of the user that I log in
This is the view
def agregar_producto(request):
if request.method == "POST":
modelform = AgregarProducto(request.POST,request.FILES)
print modelform
if modelform.is_valid():
modelform.save()
return redirect("/editar-perfil/")
else:
modelform = AgregarProducto()
return render(request, "home/AgregarProducto.html", {"form":modelform})
This is the form
class AgregarProducto(forms.ModelForm):
class Meta:
model = productos
How can I get the form to display only the sections of the model secciones of the user that logged in
Override the __init__ method of your model, and set the queryset for your secciones field.
class AgregarProducto(forms.ModelForm):
class Meta:
model = productos
def __init__(self, *args, **kwargs):
user = kwargs.pop('user')
super(AgregarProducto, self).__init__(*args, **kwargs)
self.fields['secciones'].queryset = secciones.objects.filter(user=user)
Now update your view so that you pass the user when instantiating the form. You need to do this for GET and POST requests.
if request.method == "POST":
modelform = AgregarProducto(request.POST, request.FILES, user=request.user)
...
else:
modelform = AgregarProducto(user=request.user)
You can use the login_required decorator, so that only logged in users can access the view.
from django.contrib.auth.decorators import login_required
#login_required
def agregar_producto(request):
Finally, please rename your models to Secciones and Productos. The lowercase secciones and productos look very unusual to most Django users, and can be confused with model instances.

Display auth.User fields in related model

I have created this Django model:
class Teacher(models.Model):
user = models.OneToOneField('auth.User', on_delete=models.CASCADE, default=1)
surname = models.CharField(max_length=15, null=True)
creation_date = models.DateTimeField('creation date', auto_now_add=True)
deletion_date = models.DateTimeField('deletion date', null=True, blank=True)
and a form to represent it in read-only mode:
class TeacherViewForm(ModelForm):
def __init__(self, *args, **kwargs):
super(TeacherViewForm, self).__init__(*args, **kwargs)
instance = getattr(self, 'instance', None)
if instance and instance.pk:
for field in self.fields:
self.fields[field].widget.attrs['disabled'] = True
class Meta:
model = Teacher
exclude = ['deletion_date']
The view looks like this:
class TeacherViewDetail(generic.DetailView):
model = Teacher
template_name = 'control/teacher_detail.html'
form_class = TeacherViewForm
def get_form(self):
# Instantiate the form
form = self.form_class(instance=self.object)
return form
def get_context_data(self, **kwargs):
context = super(TeacherViewDetail, self).get_context_data(**kwargs)
context.update({
'form': self.get_form(),
})
return context
As you can see, there is a OneToOne relation between the Teacher and the auth.User models. I need to displayfirst_name and last_name from auth.User model, but only the username is shown.
How can I display these fields the same way field Teacher.surname is being displayed?
Do I have to include them in the model, the form.fields or is there a property I have to modify in order to achieve it?
Thanks
You would have to remove the user field and add the appropriate fields instead. Then in your form's __init__, fill the initial values:
# forms.py
class TeacherViewForm(ModelForm):
first_name = forms.CharField()
last_name = forms.CharField()
def __init__(self, *args, **kwargs):
super(TeacherViewForm, self).__init__(*args, **kwargs)
instance = getattr(self, 'instance', None)
if instance and instance.pk:
for field in self.fields:
self.fields[field].widget.attrs['disabled'] = True
# set initial values
self.fields['first_name'].initial = instance.user.first_name
self.fields['last_name'].initial = instance.user.last_name
class Meta:
model = Teacher
exclude = ['user', 'deletion_date'] # exclude user
But why do you use a form at all if you disable all fields? You can just render all this information much more freely in the template.

Using admin site in Django

I have a model with two date fields, for submitted and published and a boolean field for approved.
class BlogModel(models.Model):
title = models.CharField(max_length=100)
article = models.CharField(max_length=255)
pub_date = models.DateTimeField('date published')
submitted_date = models.DateTimeField('date submitted')
author = models.CharField(max_length=255)
approved = models.BooleanField(default=False)
def __str__(self): # __unicode__ on Python 2
return 'approved, ' + str(self.approved) + ' article, ' + self.article
I have included this model in admin.py. Admin can approve the article, but is it possible to have the pub_date be the current time when the article is approved in admin?
UPDATE These models work for me in forms.py as suggested by Raja Simon
class BlogForm(forms.ModelForm):
class Meta:
model = BlogModel
fields = '__all__'
# fields = ('id', 'title', 'article')
def save(self, force_insert=False, force_update=False, commit=True):
m = super(BlogForm, self).save(commit=False)
# do custom stuff
m.pub_date = timezone.now()
if commit:
m.save()
return m
and admin.py
class BlogModelAdmin(admin.ModelAdmin):
form = BlogForm
fields = ('title', 'article', 'pub_date', 'submitted_date', 'author', 'approved')
# fields = '__all__' won't work here, each field needs to be added individually as above
pass
admin.site.register(BlogModel, BlogModelAdmin)
Thanks
You can override form save in admin
class BlogModelAdmin(admin.ModelAdmin):
form = BlogModelForm
pass
admin.site.register(BlogModel, BlogModelAdmin)
And in forms
class BlogModelForm(ModelForm):
class Meta:
model = BlogModel
fields = '__all__'
def save(self, force_insert=False, force_update=False, commit=True):
m = super(BlogModelForm, self).save(commit=False)
# do custom stuff
m.pub_date = timezone.now()
if commit:
m.save()
return m
Possible by over riding the save_model method in your admin
def save_model(self, request, obj, form, change):
if obj.approved:
obj.pub_date = timezone.now()
This is probably superior to overriding the save method in the model itself.

django override model save() method

I want to get request as a parameter in save() method.
models.py
class Profile(models.Model):
uuid = UUIDField(auto=True)
user = models.ForeignKey(User)
name = models.CharField(max_length=128)
dob = models.DateField()
class Meta:
db_table = 'profile'
def save(self,*args,**kwargs):
if not self.pk:
self.user_id = 2 #here i need request.user instead of 2
super(Profile,self).save(*args,**kwargs)
forms.py
class ProfileForm(forms.ModelForm):
class Meta:
model = Profile
exclude = ['uuid','user']
views.py
def add(request):
profile_form = ProfileForm(request.POST)
profile_form.save()
Instead of the value 2 i want to pass request.user. How can i do it. If question is not correct somebody please correct the question.
Don't do that in the model. Do it in the view.
profile_form = ProfileForm(request.POST)
if profile_form.is_valid():
profile = profile_form.save(commit=False)
profile.user = request.user
profile.save()
One way is to use crequest, like this:
# model
from crequest.middleware import CrequestMiddleware
class Profile(models.Model):
# Some fields
def save(self, *args, **kwargs):
crequest = CrequestMiddleware.get_request() #its the current request
if crequest.user.pk is 1:
# Do stuff
pass
super(Profile,self).save(*args,**kwargs)
You can use kwargs.
For example:
views.py
profile_form.save(your_arg = request)
models.py
your_var = kwargs.pop("your_arg", None) #Second argument of kwargs.pop is default value
In you case, pass request as keyword argument, and then use it in models save() method.