Additional Functions for Django
Project description
What this?
This repository contains additional code for Django.
Why I create this?
Because I love Django, and usually using it. However, I found some essential code was lacked for modern web development. For example, you might want to send Ajax Payload like this:
{
"name": "John Doe",
"age": 49,
"email": "john@example.com",
"email_aliases": [
"john.due@example.com",
"due_49@example.com",
"john.1968@example.com"
]
}
In this case, you can validate name, age, and email field by using Form layer on Django. However, email_aliases cannot be validated because it’s a list and it should validate each value whether it is email-formatted or not.
To support this case (and some other cases that Django can’t handle), I wrote some code to support List validation.
How To Use It
Forms
Angular form
As you can see above sections, you’ll need to implement redundant code:
from django import forms
from .models import UserInfo
class UserInfoForm(forms.ModelForm):
class Meta(object):
model = UserInfo
exclude = ("2fa_secret", )
# They are already implemented because UserInfoForm inherit ModelForm
# and the target model has the fields.
widgets = {
"age": forms.NumberInput(attrs={"data-ng-model": "model.age"}),
"phone": forms.TextInput(attrs={"data-ng-model": "model.phone"}),
"street": forms.TextInput(attrs={"data-ng-model": "model.street"}),
"city": forms.TextInput(attrs={"data-ng-model": "model.city"}),
"state": forms.TextInput(attrs={"data-ng-model": "model.state"})
}
However, you can implement simpler code by using AngularForm:
from django import forms
from djextra.forms.angular1 import AngularForm
class UserInfoForm(AngularForm, forms.ModelForm):
ng_model_prefix = "model" # Change this if you want to use other than "model"
class Meta(object):
model = UserInfo
exclude = ("2fa_secret", )
# Automatically generates AngularJS forms.
Data binding between AngularJS and Django
If you want put the value to scope model on initialization, you might have 2 ways:
Serialize your model into json by using json.dumps and django.forms.model_to_dict
Set handle_ng_init meta attribute
The first one is very clear, convert your model into dict with django.forms.model_to_dict, and serialize the dict into JSON, and finally put the text as data-ng-init to the form like this:
<form data-ng-init="model = {{ view.model_dict | tojson }}">
<!-- bla bla bla bla... -->
</form>
The second one is simple; just set handle_ng_init Meta attribute of the form to True like this:
from django import forms
from djextra.forms.angular1 import AngularForm
class UserInfoForm(AngularForm, forms.ModelForm):
ng_model_prefix = "model" # Change this if you want to use other than "model"
handle_ng_init = True
class Meta(object):
model = UserInfo
exclude = ("2fa_secret", )
# Automatically generates AngularJS forms.
If you want to specify what value to be set, you can use ng_init_format_func meta attribute like this:
from django import forms
from djextra.forms.angular1 import AngularForm
class UserInfoForm(AngularForm, forms.ModelForm):
ng_model_prefix = "model" # Change this if you want to use other than "model"
handle_ng_init = True
ng_init_format_func = {
"age": lambda value: ("{} years old").format(value)
}
class Meta(object):
model = UserInfo
exclude = ("2fa_secret", )
# Automatically generates AngularJS forms.
However, as you know, server-side is quite different from client side, so to keep that ``age`` is formatted, you might also need to write client-side code.
All required forms
If you’d like to make all fields required on ModelForm, you will re-implement entire fields like this:
from django import forms
from .models import UserInfo
class UserInfoForm(forms.ModelForm):
class Meta(object):
model = UserInfo
exclude = ("2fa_secret", )
# Assume that all fields are optional.
age = forms.IntegerField(
required=True,
widget=forms.NumberInput(attrs={"data-ng-model": "model.age"})
)
phone = forms.CharField(
required=True,
widget=forms.TextInput(attrs={"data-ng-model": "model.phone"})
)
street = forms.CharField(
required=True,
widget=forms.TextInput(attrs={"data-ng-model": "model.street"})
)
city = forms.CharField(
required=True,
widget=forms.TextInput(attrs={"data-ng-model": "model.city"})
)
state = forms.CharField(
required=True,
widget=forms.TextInput(attrs={"data-ng-model": "model.state"})
)
Moreover, you will not be able to check if the field is proper unless you refer Django’s code. To reduce this time consumption, I implemented AllReqiuredForm:
from django import forms
from djextra.forms.angular1 import AllRequiredForm
from .models import UserInfo
class UserInfoForm(AllRequiredForm, forms.ModelForm):
class Meta(object):
model = UserInfo
exclude = ("2fa_secret", )
# Assume that all fields are optional.
By using AllRequiredForm, you can reduce your LOC like above. Of course, you can put optional field as exceptions like this:
from django import forms
from djextra.forms.angular1 import AllRequiredForm
from .models import UserInfo
class UserInfoForm(AllRequiredForm, forms.ModelForm):
class Meta(object):
model = UserInfo
exclude = ("2fa_secret", )
# Assume that all fields are optional.
# By specifying optional, the specified fields won't
# become a required field.
optional = ("phone", )
FieldAttributeForm
When you set attribute, especially with ModelForm, you might need to re-set widget with widget Meta attribute like this:
from django.db import models as db
from django import forms
class NamePrice(db.Model):
name = db.CharField()
price = db.IntegerField()
class NameDescForm(forms.ModelForm):
class Meta(object):
model = NamePrice
exclude = ("id", )
widgets = {
"price": forms.NumberInput(attrs={"max": "100"})
}
This is okay when you know what widget is used and attribute max is the fixed value. However, if you don’t know what widget is used, or max is the dynamic value by the server, Django might not have suitable solution. To solve this problem, djextra has a form named FieldAttributeForm and you can use it like this:
from django.db import models as db
from django import forms
from django.conf import settings
class NamePrice(db.Model):
name = db.CharField()
price = db.IntegerField()
class NameDescForm(FieldAttributeForm, forms.ModelForm):
class Meta(object):
model = NamePrice
exclude = ("id", )
fld_attrs = {
"price": {
# The point is the attribute can be callable.
"max": lambda form, fld, name, value: 100 if value else "",
"min": "0"
},
}
In addition to this, FieldAttributeForm can set attributes that can be applied to all the fields by using common_attrs meta attribute:
from django.db import models as db
from django import forms
from django.conf import settings
class NamePrice(db.Model):
name = db.CharField()
price = db.IntegerField()
class NameDescForm(FieldAttributeForm, forms.ModelForm):
class Meta(object):
model = NamePrice
exclude = ("id", )
common_attrs = {
# Also it can be callable.
"data-on-delay": lambda form, fld, name, value: (
"delay('{}',{})"
).format(
name, value
),
"data-on-load": "test()",
}
fld_attrs = {
"price": {
"max": lambda form, fld, name, value: 100 if value else "",
"min": 0
},
}
Form Fields
ListField
ListField is used to handle a list of values like above example. To use ListField, you can write a form like this:
forms.py
from django import forms
from djextra import forms as exforms
class ExampleForm(forms.Form):
name = forms.CharField()
age = forms.IntegerField()
email = forms.EmailField()
email_aliases = exforms.ListField(field=forms.EmailField())
Then, Inputting the data as usual, the validation will start. If you don’t specify field keyword argument, django.forms.CharField object is specified.
Widgets
Widgets for Angular Materials
If you like Material Design, you’d also like to use Angular Material, but as you can see the doc. the components are using special tags. For example, select and option input controllers should be replaced with mdSelect and mdOption and they are not provided by built-in widgets.
This widget provides the widgets:
from django import forms
from djextra.forms.angular1 import (
AngularForm, MDSelect, MDMultiSelect, MDDatePicker, MDDateSelect, MDCheckBox
)
from .models import ExampleModel
class ExampleForm(AngularForm, forms.ModelForm):
class Meta(object):
model = ExampleModel
exclude = ("secret_field", )
widgets = {
"start_since": MDDateSelect(),
"available_date": MDDatePicker(),
"shape": MDSelect(choices=(
("F", "Fat"), ("N": "Normal"), ("T", "Thin")
)),
"needs_fill": MDCheckBox("Fill with border color?")
}
Contribution
Contribution of code is welcome, and the code is tested with tox. Before sending your pull request, please check you tested your code very well.
License
This repository is licensed under the terms of MIT License. Please check LICENSE.md for the detail.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.