I am trying to add classes to my forms but the classes are not being applied. I cannot find what I'm doing wrong. Any help would be greatly appreciated.
I'm hoping to set bootstrap classes, so I'd like , if possible.
class PersonalInformation(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
first_name = models.CharField(max_length=200, default='')
surname = models.CharField(max_length=200, default='')
dob = models.DateTimeField('Date of birth (mm/dd/yyyy)', null=True, default=now)
preferred_subjects = models.CharField('Are there subjects you would prefer doing?', max_length=200, default='')
class PersonalInformationForm(forms.ModelForm):
OPTIONS = (
("ANI", "Animals"),
("ART", "Art"),
("COM", "Communication"),
)
preferred_subjects = forms.MultipleChoiceField(widget=forms.CheckboxSelectMultiple(
attrs={
'class' : 'not working',
'id' : 'not working'
}
), choices=OPTIONS)
class Meta:
model = PersonalInformation
fields = ['first_name', 'surname', 'dob', 'preferred_subjects']
widgets = {
'dob': DatePickerInput(
options={
"format": "MM/DD/YYYY",
"showClose": False,
"showClear": False,
"showTodayButton": False,
}
),
}
Thank you.
You can try Updating the form field attributes on initiallization.
class PersonalInformationForm(forms.ModelForm):
OPTIONS = (
("ANI", "Animals"),
("ART", "Art"),
("COM", "Communication"),
)
preferred_subjects = forms.MultipleChoiceField(widget=forms.CheckboxSelectMultiple(), choices=OPTIONS)
class Meta:
model = PersonalInformation
fields = ['first_name', 'surname', 'dob', 'preferred_subjects']
widgets = {
'dob': DatePickerInput(
options={
"format": "MM/DD/YYYY",
"showClose": False,
"showClear": False,
"showTodayButton": False,
}
),
}
def __init__(self, request, *args, **kwargs):
super(PersonalInformationForm, self).__init__(*args, **kwargs)
self.fields['preferred_subjects'].widget.attrs.update({'class': 'form-control', 'placeholder': 'First Name'})
EDIT:
preferred_subjects = forms.MultipleChoiceField(widget=forms.CheckboxSelectMultiple(), choices=OPTIONS)
preferred_subjects.widget.attrs.update({'class': 'form-control'})
class Meta:
model = PersonalInformation
fields = ['first_name', 'surname', 'dob', 'preferred_subjects']
widgets = {
'dob': DatePickerInput(
options={
"format": "MM/DD/YYYY",
"showClose": False,
"showClear": False,
"showTodayButton": False,
}
),
}
if this doesnt work, consider using 'SelectMultiple' instead of 'CheckboxSelectMultiple'
forms.MultipleChoiceField(widget=forms.SelectMultiple(), choices=OPTIONS)
Related
I am building an api for CRUD operations on a user table which has association with country and state tables as given model definitions:
class Country(models.Model):
""" Model for Country"""
country_abbreviation = models.CharField(max_length=80)
country_name = models.CharField(max_length=80)
is_active = models.SmallIntegerField()
def __str__(self):
return self.country_name
class State(models.Model):
""" model for saving state"""
state_name = models.CharField(max_length=32)
state_abbreviation = models.CharField(max_length=8)
country = models.ForeignKey(Country, related_name='states', on_delete=models.CASCADE)
is_active = models.SmallIntegerField(default=1, blank=True)
def __str__(self):
return self.state_name
class Meta:
""" meta class"""
ordering = ('state_name', )
class User(models.Model):
""" model for saving user information """
first_name = models.CharField(max_length=20)
last_name = models.CharField(max_length=20)
country = models.ForeignKey(Country, related_name='user_country', on_delete=models.CASCADE)
state = models.ForeignKey(State, related_name='user_state', on_delete=models.CASCADE)
address = models.CharField(max_length=150)
def __str__(self):
return '%d: %s %s' % (self.id, self.first_name, self.last_name)
class Meta:
""" meta class """
ordering = ('first_name', )
I am writing serializers in a way that while I am getting records from user table, for every row in the table there must be available all the country and state info associated with that particular row instead of just their ids respectively:
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ['id', 'first_name', 'last_name', 'country', 'state', 'address']
Expected response :
{
"count": 1,
"next": null,
"previous": null,
"results": [
{
"id": 1,
"first_name": "Satish",
"last_name": "Kumar",
"country": {
"id": 23,
"country_name": "India"
},
"state": {
"id": 22,
"state_name": "Delhi"
},
"address": "New Delhi"
}
],
"page_size": 10,
"model_type": "User"
}
I am getting:
{
"count": 1,
"next": null,
"previous": null,
"results": [
{
"id": 1,
"first_name": "Satish",
"last_name": "Kumar",
"country": 23,
"state": 22,
"address": "New Delhi"
}
],
"page_size": 10,
"model_type": "User"
}
In the views.py the codes look like:
class UserList(generics.ListCreateAPIView):
filter_backends = [filters.SearchFilter, filters.OrderingFilter]
search_fields = ['first_name', 'last_name']
ordering_fields = ['id', 'first_name', 'last_name']
ordering = ['first_name']
queryset = User.objects.all()
serializer_class = UserSerializer
permission_classes = [permissions.IsAuthenticated]
def list(self, request, *args, **kwargs):
page_size_req = self.request.query_params.get('page_size', None)
if page_size_req is not None:
records_per_page = page_size_req
pagination.PageNumberPagination.page_size = int(page_size_req)
else:
records_per_page = 10
response = super().list(request, args, kwargs)
# Add additional info required:
response.data['page_size'] = records_per_page
response.data['model_type'] = 'User'
return response
Can someone please help me figure out, how I can achieve the desired results in this case? Thanks for your time in advance.
In that case, add depth to your serializer:
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ['id', 'first_name', 'last_name', 'country', 'state', 'address']
depth = 1
I wonder how to handle POST request to properly save the incoming data, having such models:
class Recipe(models.Model):
author = models.ForeignKey('auth.user', related_name='recipes', on_delete=models.CASCADE)
image = models.TextField(default='None')
name = models.CharField(max_length=100)
description = models.TextField(default='No description')
votes = models.IntegerField(default=0)
def __str__(self):
return self.name
class Ingredient(models.Model):
image = models.TextField(default='None')
name = models.CharField(max_length=100)
description = models.TextField(default='No description')
price = models.DecimalField(max_digits=8, decimal_places=3)
unit_price = models.DecimalField(max_digits=8, decimal_places=3)
unit_quantity = models.CharField(max_length=20)
def __str__(self):
return self.name
I wanted to avoid duplicating Ingredient objects, so to provide quantity of specific Ingredient in Recipe I've created a RecipesIngredient model that binds Ingredient with Recipe, but also contains a quantity of this Ingredient:
class RecipesIngredient(models.Model):
recipe = models.ForeignKey(Recipe, related_name='ingredients', on_delete=models.CASCADE)
ingredient = models.ForeignKey(Ingredient, on_delete=models.CASCADE)
quantity = models.CharField(max_length=100)
def __str__(self):
return self.quantity
I've also prepared some serializers for these models:
class IngredientSerializer(HyperlinkedModelSerializer):
class Meta:
model = Ingredient
fields = (
'url',
'image',
'name',
'description',
'price',
'unit_price',
'unit_quantity'
)
class RecipesIngredientSerializer(HyperlinkedModelSerializer):
ingredient_name = ReadOnlyField(source='ingredient.name')
ingredient_price = ReadOnlyField(source='ingredient.price')
ingredient_unit_price = ReadOnlyField(source='ingredient.unit_price')
ingredient_unit_quantity = ReadOnlyField(source='ingredient.unit_quantity')
class Meta:
model = RecipesIngredient
fields = (
'url',
'ingredient_name',
'quantity',
'ingredient_price',
'ingredient_unit_price',
'ingredient_unit_quantity'
)
class RecipeListSerializer(HyperlinkedModelSerializer):
author = ReadOnlyField(source='author.username')
author_url = ReadOnlyField(source='author.url')
class Meta:
model = Recipe
fields = (
'url',
'author',
'author_url',
'image',
'name',
'description',
'votes'
)
class RecipeDetailSerializer(HyperlinkedModelSerializer):
author = ReadOnlyField(source='author.username')
author_url = ReadOnlyField(source='author.url')
ingredients = RecipesIngredientSerializer(many=True)
class Meta:
model = Recipe
fields = (
'url',
'author',
'author_url',
'image',
'name',
'description',
'ingredients',
'votes'
)
But in this case, I have to first create a Recipe instance and save it to DB, then do the same with Ingredient to be able to "bind" them in RecipesIngredient. Is this possible to handle this case with only one POST request to view below?
#
# path('recipes/', views.RecipeList.as_view(), name='recipe-list')
#
class RecipeList(generics.ListCreateAPIView):
queryset = Recipe.objects.all()
serializer_class = RecipeListSerializer
def perform_create(self, serializer):
serializer.save(author=self.request.user)
#EDIT
I forgot 'bout this thread, but the problem is solved now. I've prepared another serializer for CREATE purposes only, and overridden the 'create' function of this serializer.:
class RecipeCreateSerializer(HyperlinkedModelSerializer):
#author = ReadOnlyField(source='author.username')
#author_url = ReadOnlyField(source='author.url')
recipes_ingredients = RecipesIngredientCreateSerializer(many=True)
def create(self, validated_data):
recipes_ingredients = validated_data.pop('recipes_ingredients')
recipe_instance = super().create(validated_data)
for recipe_ingredient in recipes_ingredients:
ingredient_data = recipe_ingredient.pop('ingredient')
ingredient_instance = Ingredient(
image=ingredient_data['image'],
name=ingredient_data['name'],
description=ingredient_data['description'],
price=ingredient_data['price'],
unit_price=ingredient_data['unit_price'],
unit_quantity=ingredient_data['unit_quantity'],
)
ingredient_instance.save()
recipes_ingredient_instance = RecipesIngredient(
recipe=recipe_instance,
ingredient=ingredient_instance,
quantity=recipe_ingredient['quantity']
)
recipes_ingredient_instance.save()
return recipe_instance
class Meta:
model = Recipe
fields = (
'url',
'image',
'name',
'description',
'votes',
'recipes_ingredients',
)
Also the JSON file looks a bit different now, but everything works just fine:
{
"image": "image-url",
"name": "recipes-name",
"description": "recipes-description",
"votes": 0,
"recipes_ingredients": [
{
"quantity": "ingredients-quantity",
"ingredient": {
"image": "image-url",
"name": ingredient-name",
"description": "ingredient-description",
"price": 3.6,
"unit_price": 0.36,
"unit_quantity": "100ML"
}
},
{
"quantity": "ingredients-quantity",
"ingredient": {
"image": "image-url",
"name": ingredient-name",
"description": "ingredient-description",
"price": 0.3,
"unit_price": 0.3,
"unit_quantity": "EACH"
}
},
{
"quantity": "ingredients-quantity",
"ingredient": {
"image": "image-url",
"name": ingredient-name",
"description": "ingredient-description",
"price": 2.0,
"unit_price": 0.8,
"unit_quantity": "KG"
}
}
]
}
I have User and Item models, and am having an issue with nested Items in a Item.objects.all() view. Specifically, I'm getting the following in the ItemListView resource:
[ {
"id": 3,
"description": "Some test item description",
"user": {
"id": 10,
"username": "jason",
"email": "test#test.com",
"token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0aW1lIjoiRnJpIE1hciAyNCAyMDo1NDo1OSAyMDE3IiwidXNlcm5hbWUiOiJqYXNvbiJ9.x4qdTF5eVKGLnrkcunm63n4d_X8xEzEYM0z48E5HKh4",
"items": [
{
"id": 3,
"description": "Some item description",
"timestamp": "2017-03-25T15:50:08.265780Z",
"user": 10
},
{
"id": 2,
"description": "test item description",
"timestamp": "2017-03-24T22:28:49.904198Z",
"user": 10
}
]
},
"timestamp": "2017-03-25T15:50:08.265780Z"
},
What I want is the User.items excluded from the output. How can I do that with the serializers and models below:
class UserSerializer(serializers.ModelSerializer):
password = serializers.CharField(write_only = True, required = False)
confirm_password = serializers.CharField(required = False)
class Meta:
model = User
fields = ('id', 'username', 'email', 'password', 'confirm_password', 'token', 'posts')
read_only_fields = ('confirm_password', )
depth = 1
def create(self, validated_data):
return User.objects.create_user(**validated_data)
def validate(self, data):
if data['password'] != data['confirm_password']:
raise ValidationError('Passwords do not match')
return data
class ItemSerializer(serializers.ModelSerializer):
user = UserSerializer(read_only = True)
def create(self, validated_data):
return Item.objects.create(**validated_data)
class Meta:
fields = ('id', 'content', 'user', 'timestamp')
read_only_fields = ('timestamp', )
model = Item
depth = 1
Models:
class User(AbstractBaseUser, PermissionsMixin):
username = models.CharField(max_length = 50,
unique = True)
email = models.EmailField(unique = True, blank = False, null = False)
token = models.CharField(max_length = 255,
default = '')
objects = UserManager()
USERNAME_FIELD = 'username'
def generate_token(self):
self.token = User.objects.generate_token(user = self)
self.save()
#python_2_unicode_compatible
def __str__(self):
return '{} [Username: {}] [Email: {}]'.format(self.pk,
self.username,
self.email)
class Meta:
verbose_name = 'user'
verbose_name_plural = 'users'
class Item(models.Model):
description = models.CharField(db_index = True, max_length = 1000)
timestamp = models.DateTimeField(auto_now_add = True, db_index = True)
user = models.ForeignKey(User,
on_delete = models.CASCADE,
related_name = 'items')
def __str__(self):
return '{} [User: {}] [Timestamp: {}] [Slug {}]'.format(self.pk, self.user.pk, self.timestamp, self.description[:20])
class Meta:
verbose_name = 'item'
verbose_name_plural = 'items'
ordering = ['-timestamp']
There is no out of the box solution to dynamically set which fields you want to serialize on related models.
You either need to define a stripped copy of UserSerializer and use it inside ItemSerializer (can define it right inside ItemSerializer class to not pollute the namespace), or extend the ModelSerializer and manually implement some support for dynamic field serialization, see here for some ideas (this could get tricky if you want to go a few layers deep I would imagine).
I am working on a deals/coupon selling website. I have following models, (excluding extra details).
class Order(models.Model):
email = models.EmailField(max_length=200, null=False)
phone_number = models.CharField(max_length=10, null=False)
shipping_address = models.TextField(blank=True,null=True)
coupon_code = models.CharField(max_length=20,null=True,blank=True)
gross_total = models.FloatField(default=0.0)
class Meta:
db_table = 'order'
class OrderDetail(models.Model):
order = models.ForeignKey(Order,related_name='order_details')
package = models.ForeignKey(Package)
quantity = models.IntegerField(null=False)
unit_price = models.FloatField(default=0.0)
class Meta:
db_table = 'order_detail'
class Coupon(models.Model):
order_detail = models.ForeignKey(OrderDetail,related_name='coupons')
code = models.CharField(max_length=200, null=False, unique=True)
maximum_usage_count = models.IntegerField(null=False)
used_count = models.IntegerField(default=0)
valid_from = models.DateTimeField(null=False)
valid_to = models.DateTimeField(null=False)
class Meta:
db_table = 'coupon'
My serializers for these are,
class CouponSerializer(serializers.Serializer):
class Meta:
model = Coupon
fields = ['id', 'code', 'maximum_usage_count', 'used_count', 'valid_from', 'valid_to', 'created_at',
'updated_at', 'is_active']
class OrderDetailSerializer(serializers.Serializer):
coupons = CouponSerializer(read_only=True)
class Meta:
model = OrderDetail
fields = ['id', 'package', 'quantity', 'unit_price', 'created_at', 'updated_at', 'is_active']
class OrderSerializer(serializers.ModelSerializer):
order_details = OrderDetailSerializer(read_only=True,many=True)
class Meta:
model = Order
fields = ['id','email', 'phone_number', 'shipping_address', 'coupon_code', 'gross_total','order_details']
In my listapiview, for fetching all orders, I have specified the order serializer. The api is working fine but is not able to serialize the reverse relation ship models. I am getting following response.
{
"id": 31,
"email": "ff#b.com",
"first_name": "ff",
"last_name": "ff",
"phone_number": "ff",
"shipping_address": "",
"coupon_code": "",
"gross_total": 1.0,
"payment_method": "ONLINE",
"order_status": "PLACED",
"created_at": "2016-10-01T17:26:00.432000",
"updated_at": "2016-10-01T17:48:50.797000",
"is_active": true,
"order_details": [
{
"coupons": {}
},
{
"coupons": {}
},
{
"coupons": {}
}
]
}
I think you should replace the inherited class with serializers.ModelSerializer in CouponSerializer and OrderDetailSerializer instead of just serializer.Serializer - just like you did in OrderSerializer.
After that you'll get some errors because your models (Coupon and OrderDetail) don't declare any of created_at, updated_at nor is_active fields. So you should add those fields to your models or remove them from the list in Meta in both serializer classes. But after that it works as expected.
I have an instance where I am creating an order form with multiple models. I want these models to be rendered in one form and was told that formsets are my answer. I have been doing research how this works and still spinning my wheels not knowing. Sorry if this is simple and I'm not seeing it.
Here are my models:
class Contact(models.Model):
first_name = models.CharField(max_length=100)
last_name = models.CharField(max_length=100)
email_address = models.EmailField(max_length=275)
address = models.CharField(max_length=10, choices=ADDRESS_CHOICES)
def __unicode__(self):
return "%s %s" % (self.first_name, self.last_name)
class BaseStationary(models.Model):
contact = models.ForeignKey(Contact, related_name='%(class)s_related')
address = models.CharField(max_length=10, choices=ADDRESS_CHOICES)
quantity = models.CharField(max_length=3, choices=QUANTITY_CHOICES)
class Meta:
abstract = True
class LetterHead(BaseStationary):
pass
class WindowEnv(BaseStationary):
pass
class NumberTenEnv(BaseStationary):
pass
class NineByTwelveEnv(BaseStationary):
pass
class TenByThirteenEnv(BaseStationary):
pass
class BusinessCard(models.Model):
contact = models.ForeignKey(Contact, related_name='businesscards')
card_first_name = models.CharField(max_length=100)
card_last_name = models.CharField(max_length=100)
title = models.CharField(max_length=100)
print_choices = models.CharField(max_length=19, choices=PRINT_CHOICES)
card_styles = models.CharField(max_length=12, choices=CARD_CHOICES)
card_email_address = models.EmailField(max_length=275)
office_phone_number = PhoneNumberField(_('main office phone number'),
blank=True, null=True)
toll_free_number = PhoneNumberField(_('toll free number'),
blank=True, null=True)
mobile_number = PhoneNumberField(_('mobile phone number'),
blank=True, null=True)
fax_number = PhoneNumberField(_('main office fax'),
blank=True, null=True)
card_mailing_address = models.CharField(max_length=10,
choices=ADDRESS_CHOICES)
card_quantity = models.CharField(max_length=3,
choices=CARD_QUANTITY_CHOICES)
class RushOrder(models.Model):
contact = models.ForeignKey(Contact, related_name='rushorders')
rush_order = models.BooleanField()
in_hand_date = models.DateField(blank=True, null=True)
class OrderNote(models.Model):
contact = models.ForeignKey(Contact, related_name='ordernotes')
add_note = models.BooleanField()
notes = models.TextField()
Here are my forms:
class ContactForm(forms.ModelForm):
address = forms.ChoiceField(required = True, widget=RadioSelect(), choices=ADDRESS_CHOICES)
class Meta:
model = Contact
class LetterHeadForm(forms.ModelForm):
address = forms.ChoiceField(required = True, widget=RadioSelect(attrs={'id': 'letterhead_address', 'required': 'True'}), choices=ADDRESS_CHOICES)
class Meta:
model = LetterHead
widgets = {
'contact': forms.HiddenInput,
'quantity': forms.Select(attrs={'id': 'letterhead_quantity'}, choices=QUANTITY_CHOICES),
}
class WindowEnvForm(forms.ModelForm):
address = forms.ChoiceField(required = True, widget=RadioSelect(attrs={'id': 'windowenv_address', 'required': 'True'}), choices=ADDRESS_CHOICES)
class Meta:
model = WindowEnv
widgets = {
'contact': forms.HiddenInput,
'quantity': forms.Select(attrs={'id': 'windowenv_quantity'}, choices=QUANTITY_CHOICES),
}
class NumberTenEnvForm(forms.ModelForm):
address = forms.ChoiceField(required = True, widget=RadioSelect(attrs={'id': 'numbertenenv_address', 'required': 'True'}), choices=ADDRESS_CHOICES)
class Meta:
model = NumberTenEnv
widgets = {
'contact': forms.HiddenInput,
'quantity': forms.Select(attrs={'id': 'numbertenenv_quantity'}, choices=QUANTITY_CHOICES),
}
class NineByTwelveEnvForm(forms.ModelForm):
address = forms.ChoiceField(required = True, widget=RadioSelect(attrs={'id': 'ninebytwelveenv_address', 'required': 'True'}), choices=ADDRESS_CHOICES)
class Meta:
model = NineByTwelveEnv
widgets = {
'contact': forms.HiddenInput,
'quantity': forms.Select(attrs={'id': 'ninebytwelveenv_quantity'}, choices=QUANTITY_CHOICES),
}
class TenByThirteenEnvForm(forms.ModelForm):
address = forms.ChoiceField(required = True, widget=RadioSelect(attrs={'id': 'tenbythirteenenv_address', 'required': 'True'}), choices=ADDRESS_CHOICES)
class Meta:
model = TenByThirteenEnv
widgets = {
'contact': forms.HiddenInput,
'quantity': forms.Select(attrs={'id': 'tenbythirteenenv_quantity'}, choices=QUANTITY_CHOICES),
}
class BusinessCardForm(forms.ModelForm):
print_choices = forms.ChoiceField(required = True, widget=RadioSelect(), choices=PRINT_CHOICES)
card_styles = forms.ChoiceField(required = True, widget=RadioSelect(), choices=CARD_CHOICES)
card_mailing_address = forms.ChoiceField(required = True, widget=RadioSelect(), choices=ADDRESS_CHOICES)
class Meta:
model = BusinessCard
widgets = {
'contact': forms.HiddenInput,
}
class RushOrderForm(forms.ModelForm):
class Meta:
model = RushOrder
widgets = {
'contact': forms.HiddenInput,
'rush_order': forms.CheckboxInput,
'in_hand_date': forms.extras.SelectDateWidget
}
class OrderNoteForm(forms.ModelForm):
class Meta:
model = OrderNote
widgets = {
'contact': forms.HiddenInput,
'add_note': forms.CheckboxInput,
'notes': forms.Textarea
}
And here is my view:
class OrderFormView(CreateView):
model = Contact
form_class = ContactForm
template_name = 'orderform.html'
success_url = 'success'
def get_context_data(self, **kwargs):
context = super(OrderFormView, self).get_context_data(**kwargs)
context.update({
'letterhead_form': LetterHeadForm,
'windowenv_form': WindowEnvForm,
'numbertenenv_form': NumberTenEnvForm,
'ninebytwelveenv_form': NineByTwelveEnvForm,
'tenbythirteenenv_form': TenByThirteenEnvForm,
'businesscard_form': BusinessCardForm,
'rushorder_form': RushOrderForm,
'ordernote_form': OrderNoteForm,
})
return context
def form_valid(self, form):
if form.is_valid():
data = form.cleaned_data
email = OrderFormNotification(to=[settings.ORDERFORM_EMAIL_ADDRESS, ],
extra_context=data)
email.send()
Thanks in advance for any insight. Even if it's to point me in the direction to better understand formsets for this.
If you need 9 forms for 9 different models then I don't believe formsets will help you. Formsets are for constructing multiple forms of the same type. Likewise the CreateView is intended to be used only in the simple case of creating a single model. If you are creating multiple models/validating multiple forms you will find yourself fighting with CreateView to make this work. You would be better off writing your own view class built from ProcessFormView perhaps even View.