Set Django ModelForm visible fields at runtime? - python

I have a Django model:
class Customer(models.Model):
first_name=models.CharField(max_length=20,null=True, blank=True)
last_name=models.CharField(max_length=25,null=True, blank=True)
address=models.CharField(max_length=60,null=True, blank=True)
address2=models.CharField(max_length=60,null=True, blank=True)
city=models.CharField(max_length=40,null=True, blank=True)
state=models.CharField(max_length=2,null=True, blank=True)
From there, I created a ModelForm:
class CustomerForm(forms.ModelForm):
class Meta:
model=Customer
I'd like to be able to show pieces of the form in my template corresponding to specific information the users can change. For example, if I want to let the customers change their name, I'd like to be able to show a form that only has the fields 'first_name' and 'last_name'.
One way to do this would be to create a ModelForm for each of the various field snippets... for the name example, it would look something like:
class CustomerFormName(forms.ModelForm):
class Meta:
model=Customer
fields=('first_name','last_name')
This seems pretty inelegant, and inflexible. What I'd like to do is be able to specify the fields at runtime, so when I pass the dictionary from the view to the template, I can just set which fields I'd like to show. How can I set it up so that I set the fields for a form at runtime? I'd ideally like the final dictionary passed to look something like this:
{'name_change_form':CustomerFormName(<form with only first_name and last_name>), 'address_change_form':CustomerFormName(<form with only address fields>)}
Then, I know that whenever I output name_change_form.as_p, it'll have exactly the form fields that I'm looking for.
Thoughts? Also feel free to recommend a better way to do it.

from django.forms import ModelForm
from wherever import Customer
def formClassFactory(model,fields):
ff = fields
mm = model
class formClass(ModelForm):
class Meta:
model = mm
fields = ff
return formClass
form_class = formClassFactory( ('first_name','last_name') )

Related

Trying to extend AbstractUser to create multiple user types in Django

So I have been searching all around the internet for a full example of how to user AbstractUser when u have at least 2 different models. Didn't find anything conclusive.. at least that would work on latest version of Django (2.0.1).
I have 2 models, teacher and student, and registration needs to be different. Besides username, email, name and surname, I need for example, for the student, to upload a profile picture, email, phone, student_ID. And for teacher, bio, academic title and website. Did I start good ? What is the right approach ?
class Profile(AbstractUser):
photo = models.ImageField(upload_to='students_images')
email = models.EmailField()
phone = models.CharField(max_length=15, )
class Student(Profile):
student_ID = models.CharField(unique=True, max_length=14,
validators=[RegexValidator(regex='^.{14}$',
message='The ID needs to be 14 characters long.')])
def __str__(self):
return self.name
class Teacher(Profile):
academic_title = models.CharField(max_length=30)
bio = models.TextField()
website = models.URLField(help_text="E.g.: https://www.example.com", blank=True)
Your goals can be accomplished using a 'Profile' pattern. You don't necessarily need to use a custom user model for this. But you need to have a single common model to for authentication; you can use the builtin django user for this or a custom class... Your Student and Teacher models should be OnetoOne relationships. This is the recommended solution per the documentation.
If you wish to store information related to User, you can use a OneToOneField to a model containing the fields for additional information. This one-to-one model is often called a profile model, as it might store non-auth related information about a site user.
In your case, you may do something like this:
class StudentProfile(models.Model):
user = models.OneToOneField('User', related_name='student_profile')
# additional fields for students
class TeacherProfile(models.Model):
user = models.OneToOneField('User', related_name='teacher_profile')
# additional fields for teachers
Then you can create your registration forms based on these profile models.
class StudentResistrationForm(forms.ModelForm):
class Meta:
model = StudentProfile
fields = (...)
class TeacherRegistrationForm(forms.ModelForm):
class Meta:
model = TeacherProfile
fields = (...)
You can create the user instance to which the profile is related to at the same time you create the profile. You might do this with formsets, for example.
add
class Meta:
abstract = True
to profile model
and change AbstractUser to models.Model

I have a django model but i only want to put some of my models as forms.

For example, lets say this is my model:
class Author(models.Model):
author = models.CharField()
friends = models.CharField()
and I only want to have the friends as a form field
Are you using a ModelForm? If so, you set the fields you want using the fields variable in the Meta class:
from django import forms
from yourapp.models import Author
class AuthorForm(forms.ModelForm):
class Meta:
model = Author
fields = [ "friends", ]
Note: The fields variable is a list, so make sure you include the trailing comma if you're only passing a single field.
Suggest that you have a read of the Django ModelForm Docs as well, as that way you'll get a bit ahead on all the other questions you're probably about to come up with :)

Django: allow user to add fields to model

I am just starting with Django and want to create a model for an application.
I find Djangos feature to
- automatically define validations and html widget types for forms according to the field type defined in the model and
- define a choice set for the field right in the model
very usefull and I want to make best use of it. Also, I want to make best use of the admin interface.
However, what if I want to allow the user of the application to add fields to the model? For example, consider a simple adress book. I want the user to be able to define additional atributes for all of his contacts in the admin settings, i.e. add a fax number field, so that a fax number can be added to all contacts.
from a relational DB perspective, I would have a table with atributes (PK: atr_ID, atr_name, atr_type) and an N:N relation between atributes and contacts with foreign keys from atributes and contacts - i.e. it would result in 3 tables in the DB. right?
but that way I cannot define the field types directly in the Django model. Now what is best practice here? How can I make use of Djangos functionality AND allow the user to add aditional/custom fields via the admin interface?
Thank you! :)
Best
Teconomix
i would suggest storing json as a string in the database, that way it can be as extendable as you want and the field list can go very long.
Edit:
If you are using other damn backends you can use Django-jsonfield. If you are using Postgres then it has a native jsonfield support for enhanced querying, etc.
Edit 2:
Using django mongodb connector can also help.
I've used this approach, first seen in django-payslip, to allow for extendable fields. This provides a structure for adding fields to models, from which you can allow users to add/edit through standard view procedures (no admin hacking necessary). This should be enough to get you started, and taking a look at django-payslip's source code (see the views) also provides view Mixins and forms as an example of how to render to users.
class YourModel(models.Model):
extra_fields = models.ManyToManyField(
'your_app.ExtraField',
verbose_name=_('Extra fields'),
blank=True, null=True,
)
class ExtraFieldType(models.Model):
"""
Model to create custom information holders.
:name: Name of the attribute.
:description: Description of the attribute.
:model: Can be set in order to allow the use of only one model.
:fixed_values: Can transform related exta fields into choices.
"""
name = models.CharField(
max_length=100,
verbose_name=_('Name'),
)
description = models.CharField(
max_length=100,
blank=True, null=True,
verbose_name=_('Description'),
)
model = models.CharField(
max_length=10,
choices=(
('YourModel', 'YourModel'),
('AnotherModel', 'AnotherModel'), # which models do you want to add extra fields to?
),
verbose_name=_('Model'),
blank=True, null=True,
)
fixed_values = models.BooleanField(
default=False,
verbose_name=_('Fixed values'),
)
class Meta:
ordering = ['name', ]
def __unicode__(self):
return '{0}'.format(self.name)
class ExtraField(models.Model):
"""
Model to create custom fields.
:field_type: Connection to the field type.
:value: Current value of this extra field.
"""
field_type = models.ForeignKey(
'your_app.ExtraFieldType',
verbose_name=_('Field type'),
related_name='extra_fields',
help_text=_('Only field types with fixed values can be chosen to add'
' global values.'),
)
value = models.CharField(
max_length=200,
verbose_name=_('Value'),
)
class Meta:
ordering = ['field_type__name', ]
def __unicode__(self):
return '{0} ({1}) - {2}'.format(
self.field_type, self.field_type.get_model_display() or 'general',
self.value)
You can use InlineModelAdmin objects. It should be something like:
#models.py
from django.db import models
class Person(models.Model):
name = models.CharField(max_length=100)
class ContactType(models.Model):
name = models.CharField(max_length=100)
class Contact(models.Model):
person = models.ForeignKey(Person, on_delete=models.CASCADE)
contact_type = models.ForeignKey(ContactType, on_delete=models.CASCADE)
value = models.CharField(max_length=100)
#admin.py
from django.contrib import admin
class ContactInline(admin.TabularInline):
model = Contact
class PersonAdmin(admin.ModelAdmin):
inlines = [
ContactInline,
]
By the way... stackoverflow questions should contain some code. You should try to do something before asking a question.

Django model select choice, populated with fields from other model instances

Suppose I have a model Car that has a field brand and a model owner that has two field: name and car_brand, whereas the latter should be one of the Car instances brands. I need to have a form where the user is presented with a text field name and a drop down select choice populated with all the brand name of Car instances. How would I achieve that?
Here is the code I am starting with. Feel free to correct. Thanks.
models.py
from django.db import models
class Car(models.Model):
brand = models.CharField(max_length=20)
class Owner(models.Model):
name = models.CharField(max_length=20)
car_brand = models.ForeignKey(Car)
forms.py
from django.forms import ModelForm, TextInput, Select
from app.models import Owner
class OwnerForm(ModelForm):
class Meta():
model = Owner
fields = ("name", "car_brand")
widgets = {
"name" : TextInput(attrs={"class" : "name"}),
"car_brand" : Select(attrs={"class" : "car_brand"}),
}
You could probably just define a __unicode__ method on your Car model and I think it should work fine. As Daniel mentioned, the form might be getting confused since you've overridden the widgets. I could be wrong, but I thought django automatically rendered attributes on form elements that can be used for styling purposes. Maybe you don't need to override the widgets. If not, you can specify the form field explicitly:
class OwnerForm(ModelForm):
car_brand = forms.ModelChoiceField(
queryset=Car.objects.all(),
widget=Select(attrs={'class': 'car_brand'}),
)
class Meta:
model = Owner
fields = ('name', 'car_brand')
widgets = {
'name': TextInput(attrs={'class': 'name'})
}
As a side note, is there any reason you don't have a CarBrand model and a foreign key field relating the Car model to it? That would be a more normalized approach to modeling your data.

Implementing USZipCodeField and USStateField in django

I'm looking to implement a zipcode field in django using the form objects from localflavor, but not quite getting them to work. I want to have a zipcode field in a form (or ModelForm in my case), but the fields never validate as a zipcode when calling _get_errors() on the form object. The way I'm implementing it seems right to me but is apparently wrong, does anyone know what the right way to do this might be?
I have a ModelForm that I want to use zipcode (and also USStateField) in:
from django.contrib.localflavor.us.forms import USStateField
from django.contrib.localflavor.us.forms import USZipCodeField
class FooForm(ModelForm):
class Meta:
model = Bar
fields = ('address', #This form uses a subset of fields from the model
'address_apt',
'address_city',
'address_state',
'address_zip',
'home_phone',
'mobile_phone')
widgets= {
'address_zip' : USZipCodeField(),
'address_state' : USStateField(),
}
The ModelForm 'FooForm' links to a model that looks like:
from django.contrib.localflavor.us import models as usmodels
class Bar(models.Model):
db_table = 'BAR'
address = models.CharField(max_length=255)
address_apt = models.CharField(max_length=40, blank=True)
address_city = models.CharField(max_length=90)
address_state = usmodels.USStateField()
address_zip = models.CharField(max_length=15)
home_phone = usmodels.PhoneNumberField( )
mobile_phone = usmodels.PhoneNumberField( )
#... There are more fields in the model...
But if I create an instance of the form and run it's validation, it never cares about the form level validation, only the model level validation:
foo_instance = FooForm(request.POST)
#Let's assume request.POST looks like:
#<QueryDict: {u'address_city': [u'asdf'], u'mobile_phone': [u'asdf'], u'address_state': [u'California'], u'home_phone': [u'asdf'], [u'1'], u'address': [u'123 foo'], u'address_zip': [u'asdf']}>
foo_instance._get_errors()
Yields:
<ul class="errorlist">
<li>mobile_phone<ul class="errorlist">
<li>Phone numbers must be in XXX-XXX-XXXX format.</li></ul>
</li><li>home_phone<ul class="errorlist">
<li>Phone numbers must be in XXX-XXX-XXXX format.</li></ul>
</li></ul>
I need to be able to call validation on the populated form object and have it tell me that the zipcode is formated improperly if so. Doing something wrong, just don't know what atm.
Using widgets declaratively has literally only just been added to the trunk SVN version in the last day or so. If you're using an older checkout, or a released version, it won't work - you'll need to go back to the old way of doing it, by overriding the field declarations at the top level of the form.

Categories