I use Django 1.11.10 and python 3.6; I have Category model that has name and parent. Parent field links to itself. But when I create new Category model, I want choose parent from already created categories that have no parents. So how to predefine this list?
class Category(models.Model):
name = models.CharField(max_length=50, unique=True)
parent = models.ForeignKey('self', null=True, blank=True)
------
class CategoryForm(forms.ModelForm):
class Meta:
model = Category
fields = ['name', 'parent']
class CategoryAdmin(admin.ModelAdmin):
form = CategoryForm
admin.site.register(Category, CategoryAdmin)
I think you want a drop down in the form when the a accesses the front end.
You can add this to your form:
parent = forms.forms.ModelChoiceField(queryset= Category.objects.all(), required = True)
So the final form would look like:
class CategoryForm(forms.ModelForm):
parent = forms.forms.ModelChoiceField(queryset= Category.objects.all(), required = True)
class Meta:
model = Category
fields = ['name', 'parent']
Let me know if this is what you wanted!
Related
I have two django models that are related with a many to many field:
class Member(models.Model):
user = models.OneToOneField(to=settings.AUTH_USER_MODEL)
date_of_birth = models.DateField()
bio = models.TextField()
class Book(models.Model):
name = models.CharField(max_length=255)
author= models.CharField(max_length=255)
description = models.TextField()
read_by = models.ManyToManyField(to=Member, related_name='books_read')
The serializers for these models are:
class MemberSerializer(serializers.Model):
id = serializers.IntegerField(read_only=True)
user_id = serializers.IntegerField(read_only=True)
class Meta:
model = Member
fields = ['id', 'user_id', 'date_of_birth', 'bio']
class BookSerializer(serializers.Model):
id = serializers.IntegerField(read_only=True)
class Meta:
model = Book
fields = ['id', 'name', 'author', 'bio']
I want to create an endpoint to be able to add a book to a member. The only way I could write a serializer for it was:
class BookIdSerializer(serializers.Model):
class Meta:
model = Book
fields = ['id']
def update(self, **kwargs):
# logic to add book with passed id to the authenticated user's member profile
This however feels very wrong for two obvious reasons:
1 - There is an entire serializer object just to receive a book id
2 - It is not even generic because it performs a very specific function of adding a book with passed book id to a member
I am sure there is a better way of doing this. Kindly guide me if you know.
You can use a PrimaryKeyRelatedField:
class MemberSerializer(serializers.Model):
id = serializers.IntegerField(read_only=True)
user_id = serializers.IntegerField(read_only=True)
books = serializers.PrimaryKeyRelatedField(many=True, queryset=Book.objects.all())
class Meta:
model = Member
fields = ['id', 'user_id', 'date_of_birth', 'bio', 'books']
This will place the related books in an array, and you can edit that array to add and remove books, allowing adding or removing many at a time.
See How to use PrimaryKeyRelatedField to update categories on a many-to-many relationship for a related question/answer.
I have two models, which look like this:
class Item(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=60)
sku = models.CharField(max_length=60)
description = models.TextField()
price = models.DecimalField(max_digits=6, decimal_places=2)
location = models.CharField(max_length=60)
serial_number = models.CharField(max_length=60)
def __str__(self):
return self.name
class Warehouse(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=60)
def __str__(self):
return self.name
and they have two serializers which look like this:
class ItemSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Item
fields = ('id', 'name', 'sku', 'description', 'price', 'location', 'serial_number')
#we need a validator that checks if location is in the list of warehouses
#we need a validator that checks if sku is in the list of products
class WarehouseSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Warehouse
fields = ('id', 'name')
I need a way to ensure that the location field for newly created items matches an existing name field from a warehouse. I also need the deletion of a warehouse to trigger the deletion of all items in that warehouse, or, failing that; if the warehouse has items, it cannot be deleted.
I'm brand new to python and django, so any help would be massively appreciated!
for reference, my views class looks like
class ItemViewSet(viewsets.ModelViewSet):
queryset = Item.objects.all().order_by('name')
serializer_class = ItemSerializer
class WarehouseViewSet(viewsets.ModelViewSet):
queryset = Warehouse.objects.all().order_by('name')
serializer_class = WarehouseSerializer
if that helps, but from what I can see I don't expect it to.
Thanks in advance!
I think the problem here is your data models. It's clear that a warehouse and an item have a one to many relationship. With that, you would have something like this in your models.
from django.db import models
class Warehouse(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=60)
def __str__(self):
return self.name
class Item(models.Model):
id = models.AutoField(primary_key=True)
warehouse = models.ForeignKey(Warehouse, related_name="items", on_delete=models.CASCADE)
name = models.CharField(max_length=60)
sku = models.CharField(max_length=60)
description = models.TextField()
price = models.DecimalField(max_digits=6, decimal_places=2)
location = models.CharField(max_length=60)
serial_number = models.CharField(max_length=60)
def __str__(self):
return self.name
The on_delete=models.CASCADE will ensure that all items related to a warehouse are deleted when a warehouse is deleted. The foreign key relationship will ensure that warehouse id you provide when creating the item, exists before the item is created.
The other files will look as follows.
serializers.py
from rest_framework import serializers
from .models import Warehouse, Item
class ItemSerializer(serializers.ModelSerializer):
class Meta:
model = Item
fields = ('id', 'name', 'sku', 'description', 'price', 'location', 'serial_number', "warehouse")
class WarehouseSerializer(serializers.HyperlinkedModelSerializer):
items = serializers.StringRelatedField(many=True, required=False)
class Meta:
model = Warehouse
fields = ('id', 'name', 'items')
views.py
from .models import Item, Warehouse
from .serializers import ItemSerializer, WarehouseSerializer
from rest_framework import viewsets
class ItemViewSet(viewsets.ModelViewSet):
queryset = Item.objects.all().order_by('name')
serializer_class = ItemSerializer
class WarehouseViewSet(viewsets.ModelViewSet):
queryset = Warehouse.objects.all().order_by('name')
serializer_class = WarehouseSerializer
I have an MPTT model class, and want to use it in Django-Filter.
My MPTT model is;
class ProdCategory(MPTTModel):
name = models.CharField(max_length=200, unique=True)
parent = TreeForeignKey('self', on_delete=models.CASCADE, null=True, blank=True, related_name='children')
slug = AutoSlugField(populate_from='name',slugify=slugify)
class MPTTMeta:
order_insertion_by = ['name']
def __str__(self):
return self.name
The class is used as a foreign key in another "deal" model class as 'productCategory'
My filter class in filters.py is:
class DealFilter(django_filters.FilterSet):
class Meta:
model = deal
fields = ['store', 'productCategory']
Finally, my view is:
def filterdeals(request):
deals = deal.objects.all()
myFilter = DealFilter(request.GET, queryset = deals)
deals = myFilter.qs
args = {
"deals": deals, "myFilter":myFilter,}
return render(request, 'deals/filter.html', args)
Now, since in MPTT, I want all the children selected on filtering the parent, I want to include this code for the same:
deal.objects.filter(productCategory__in=ProdCategory.objects.get(pk=2).get_descendants(include_self=True))
But I don't know where to include this code- so that when the parent category ID is passed, I get all the children under it.
TIA for your help
Using filterset field with method will work in your case.
class DealFilter(django_filters.FilterSet):
deep_search_field = django_filters.Charfield(method='search_children', Label='Category')
class Meta:
model = deal
fields = ['store', 'productCategory', 'deep_search_field']
def search_children(self, queryset, name, value):
return queryset.filter(productCategory__in=ProdCategory.objects.get(pk=value).get_descendants(include_self=True))
When I post via the API, I want the serializer not duplicated a tag if one exists with the same name.
I tried adding "unique" to the model field of "name" in the class Tag but this did not work- it wouldn't allow me to create other Movie's that linked to a tag which exists.
Check if the field "name" already exists (case insensitive).
If the tag "name" exists, just create the FK relationship with the
existing tag name & the new movie
If the tag "name" doesn't exist, create it
Models.py
class Tag(models.Model):
name = models.CharField("Name", max_length=5000, blank=True)
taglevel = models.IntegerField("Tag level", blank=True)
def __str__(self):
return self.name
class Movie(models.Model):
title = models.CharField("Whats happening?", max_length=100, blank=True)
tag = models.ManyToManyField('Tag', blank=True)
def __str__(self):
return self.title
Serializers.py
class TagSerializer(serializers.ModelSerializer):
taglevel = filters.CharFilter(taglevel="taglevel")
class Meta:
model = Tag
fields = ('name', 'taglevel', 'id')
class MovieSerializer(serializers.ModelSerializer):
tag = TagSerializer(many=True, read_only=False)
info = InfoSerializer(many=True, read_only=True)
class Meta:
model = Movie
fields = ('title', 'tag')
def get_or_create(self, validated_data):
tags_data = validated_data.pop('tag')
task = Task.objects.get_or_create(**validated_data)
for tag_data in tags_data:
task.tag.get_or_create(**tag_data)
return task
The get_or_create doesn't work (trace here: http://dpaste.com/2G0HESS) as it tells me AssertionError: The .create() method does not support writable nested fields by default.
You'll have to write custom create method for your models. Here is an example.
I want to have a parent category of blank/null for a model while entering through a from in Django. Is it possible to pass a null value through the blank form ?
Here's my models.py
from django.db import models
class Category(models.Model):
category_name = models.CharField(max_length=300)
category_code = models.CharField(max_length=100)
category_parent = models.ForeignKey('self', blank=True, null=True)
category_image = models.ImageField(upload_to='category')
def __str__(self):
return self.category_name
And forms.py
from django import forms
from backend.models import Category
class CategoryForm(forms.ModelForm):
category_parent = forms.ModelChoiceField(
queryset=Category.objects.all(), empty_label='None')
category_image = forms.ImageField()
class Meta:
model = Category
fields = ('category_name', 'category_code',)
I want to have a null value for the parent select field if nothing is selected. Or if I'm entering the first value in the category, it should point null when there's no parent.
I hope I'm getting this right, but I'm guessing that even with the empty_label set to None you are still seeing the first category in the list selected.
When you override a ModelForm field, like you do now for category_parent, you lose the automatically connected required=False form correspondent for the blank=True in the model.
Try adding required=False to the form field, like this:
category_parent = forms.ModelChoiceField(queryset=Category.objects.all(), empty_label='None', required=False)