Pass a blank object to a self instance model - python

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)

Related

How to set `null=True` for a field using `label_from_instance`?

I am trying to set null=True and blank=True for a field using label_from_instance.
But it cannot be set by defining a field in the model class.
If I try to set it like the code below, I get an error.
fk_id = NameChoiceField(queryset=PkModel.objects.all().order_by('pk_name').distinct(), null=True, blank=True)
TypeError: init() got an unexpected keyword argument 'null'
How could I set null and blank in this case?
Here are the codes:
forms.py
from django import forms
from django.forms import ModelChoiceField
from .models import *
class NameChoiceField(ModelChoiceField):
def label_from_instance(self, obj):
return f'{obj.pk_name}'
class IndexForm(forms.ModelForm):
fk_id = NameChoiceField(queryset=PkModel.objects.all().order_by('pk_name').distinct())
class Meta:
model = FkModel
fields = '__all__'
models.py
from django.db import models
class PkModel(models.Model):
pk_id = models.IntegerField(verbose_name='pk_id',primary_key=True)
pk_name = models.CharField(max_length=20, verbose_name='pk_name')
class Meta:
verbose_name_plural = 'PkModel'
class FkModel(models.Model):
fk_code = models.CharField(max_length=20, verbose_name='fk_code', primary_key=True)
fk_id = models.ForeignKey(PkModel, to_field='pk_id', db_column='fk_id', verbose_name='fk_id',
on_delete=models.CASCADE, null=True, blank=True)
class Meta:
verbose_name_plural = 'FkModel'
Python 3.8
Django 3.2
Use required=False.
null and blank are for model fields, and describe valid data at the database level. In a form, you instead declare whether the field must be set for the form to be valid.

django all fields not appearing in admin form

I have following model in django:
class ProjectScheme(models.Model):
name = models.CharField(max_length=250, blank=False,null=False)
parent_scheme_id = models.ForeignKey(ProjectSchemeMaster, on_delete = models.CASCADE)
rule = models.TextField(blank=True)
created_on = models.DateTimeField(auto_now=False, auto_now_add=True)
created_by = models.IntegerField(blank=True,null=True)
updated_on = models.DateTimeField(auto_now=True, auto_now_add=False)
updated_by = models.IntegerField(blank=True,null=True)
def __str__(self):
return str(self.name)
And in admin.py
# Register your models here.
class ProjectSchemeAdmin(admin.ModelAdmin):
exclude = ('id',)
class Meta:
model = ProjectScheme
admin.site.register(ProjectScheme, ProjectSchemeAdmin)
But date fields: updated_on, created_on don't show up on admin form.
I also tried without ProjectSchemeAdmin class, and also:
class ProjectSchemeAdmin(admin.ModelAdmin):
pass
Also,
# list_display = [field.name for field in ProjectScheme._meta.fields if field.name != "id"]
list_display = ["id", "name","parent_scheme_id","rule","created_on", "created_by","updated_on","updated_by"]
But the same.
I need to get all the fields in admin form.
You need to add readonly_fields = ('created_on','updated_on') to display the date field.
# Register your models here.
class ProjectSchemeAdmin(admin.ModelAdmin):
list_display = ["name","parent_scheme_id","rule","created_on", "created_by","updated_on","updated_by"]
readonly_fields = ('created_on','updated_on')
class Meta:
model = ProjectScheme
admin.site.register(ProjectScheme, ProjectSchemeAdmin)
As mentioned in the docs:
As currently implemented, setting auto_now or auto_now_add to True will cause the field to have editable=False and blank=True set.
The value auto_now_add is True. So no matter what date value you enter via admin UI, it will ultimately be overriden, which means there is no reason to display the field. In this case, as you need to display the field anyway, adding the readonly_fields does the trick.
Hope this helps!

Django admin: how to predefine field values?

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!

How to display a dropdown field __str__ in Django templates?

I've got a ModelForm where I can display either a foreignkey field which comes as a dropdown list ({{ form.auto_part }}) or the value or the ID of that foreignkey field which comes as a number ({{ form.auto_part.value }}). But I want to show the __str__ value of the foreignkey field. How can I do that?
forms.py
class AddCostPriceForm(forms.ModelForm):
class Meta:
model = Product
fields = ['auto_part', 'cost_price']
models.py
class Product(Timestamped):
product_list = models.ForeignKey(List)
auto_part = models.ForeignKey(AutoPart)
quantity = models.SmallIntegerField()
unit = models.CharField(max_length=20, default='pcs')
cost_price = models.IntegerField(blank=True, null=True)
class AutoPart(Timestamped):
brand = models.ForeignKey(Brand)
auto_type = models.ForeignKey(AutoType)
part_no = models.CharField(max_length=50)
description = models.CharField(max_length=255)
def __str__(self):
return "{brand} {auto_type} - {part_no}".format(brand=self.brand, auto_type=self.auto_type, part_no=self.part_no)
Using ModelChoiceField should allow you to do this, it is the default behavior. You can configure the labels.
See https://docs.djangoproject.com/en/1.10/ref/forms/fields/#modelchoicefield
Example:
class AddCostPriceForm(forms.ModelForm):
auto_part = forms.ModelChoiceField(queryset=AutoPart.objects.all())
class Meta:
model = Product
fields = ['auto_part', 'cost_price']

Django admin InlineModels for manytomany fields

I have designed following models for my blog
from django.db import models
class Post(models.Model):
title = models.CharField(max_length=200)
body = models.TextField(default='')
created_at = models.DateTimeField('created date', auto_now_add=True, auto_now=False)
updated_at = models.DateTimeField('updated date', auto_now_add=False, auto_now=True)
author = models.ForeignKey('Author', default='admin')
def __str__(self):
return self.title
class Author(models.Model):
name = models.CharField(max_length=150)
email = models.EmailField(blank=True)
bio = models.TextField()
def __str__(self):
return self.name
class Category(models.Model):
cat_name = models.CharField(max_length=200)
post = models.ManyToManyField('Post')
def __str__(self):
return self.cat_name
class Tag(models.Model):
tag_name = models.CharField(max_length=200)
post = models.ManyToManyField('Post')
def __str__(self):
return self.tag_name
and I am trying to register this model under django admin in such a way that. I can edit the Category, Tags and Authors from the Post page. but I am having hard time to accomplish this talk, I have written this code in admin.py file
from django.contrib import admin
from .models import Post, Author, Tag, Category
class AuthorInline(admin.TabularInline):
model= Author
class TagInline(admin.StackedInline):
model= Tag
class CategoryInline(admin.StackedInline):
model = Category
#admin.register(Post) #another method of registration admin.site.register(Post, PostAdmin)
class PostAdmin(admin.ModelAdmin):
#Show the following fields in this order
fields = ['body', 'title']
#show the following filelds for nice formattng
list_display = ['title', 'author', 'created_at']
#display based on the date hirerarchy
date_hierachy = 'created_at'
#embed the following child models in this parent models
inlines = [AuthorInline, TagInline, CategoryInline,]
#to exclude fields
exclude = ('author',)
When I run my server I got the errors like
ERRORS:
<class 'blogs.admin.AuthorInline'>: (admin.E202) 'blogs.Author' has no ForeignKey to 'blogs.Post'.
<class 'blogs.admin.CategoryInline'>: (admin.E202) 'blogs.Category' has no ForeignKey to 'blogs.Post'.
<class 'blogs.admin.TagInline'>: (admin.E202) 'blogs.Tag' has no ForeignKey to 'blogs.Post'.
when investigating the error, we cannot have StackedInline class if the models doesn't have foreign key, but How can I put the Tags, Category and Author rendered formm under the Post page in django admin,
For using AuthorInline, you ned a foreignkey field in you Author model
ex:
class Author(models.Model):
post = models.ForeignKey('Post')
This means one post may have multiple authors.
But here in your situation you have the correct model and fileds which have one author for one post, so you can remove AuthorInline.
And incase of Tag and Category, you are using many-to-many field, It will be good if you go through this documentation https://docs.djangoproject.com/en/dev/ref/contrib/admin/#working-with-many-to-many-models
You have to rewrite the CategoryInline and TagInline;
class TagInline(admin.StackedInline):
model= Tag.post.through
class CategoryInline(admin.StackedInline):
model = Category.post.through
This isn't what inlines are for, and you don't want them here.
Inlines are for the reverse relation: given an author, edit their details and enter all their books on the same page. Your foreign keys and many-to-many fields are best shown as simple widgets, which is what Django does by default; the author and category will be displayed as a dropdown allowing you to choose an item, and the tags will be displayed as a multi-select box.
You might also choose to register Book as an inline on the Author admin; that's up to you.
Finally I made, what I wanted, the main gist is to make the category, author and tags choosable from the post page, so to do that, we need to add all the fields in the post model, which is the modified model
from django.db import models
from django.utils import timezone
class Author(models.Model):
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
email = models.EmailField(blank=True)
bio = models.TextField()
class Tag(models.Model):
tag_name = models.CharField(max_length=50)
class Category(models.Model):
cat_name = models.CharField(max_length=50)
class Post(models.Model):
'''post can have many categories
and categories can have many post
author can have many post but post
can have single author
post can have many tags, and tags
can have many posts'''
title = models.CharField('post title', max_length=200)
body = models.TextField(default='', null=True)
created_at = models.DateTimeField(auto_now_add=True, auto_now=False)
updated_at = models.DateTimeField(auto_now_add=False, auto_now=True)
author = models.ForeignKey(Author, verbose_name = "List of Author") #many to one relationship
def __str__(self):
return self.title
#Generally many to many fields should into that model which is going to be edited.
tags = models.ManyToManyField(Tag)
categories = models.ManyToManyField(Category)
class Meta:
ordering = ['-created_at']
verbose_name_plural = "Posteeees"
# def post_status(self):
# return timezone.now() - self.updated_at <= 1
#Recursive realation, we can define the foreignkey itself to the model and this is called rrecursive realation
#

Categories