Multiple Item Types in one class

Case:
i want to unify my items models in models.py
i have toys and furniture and books and each of this model have different fields types furniture has length and height and frame, toys don’t have frame or colors also books don’t have frame or length.

i want to query all items in home page to show all my items or 8 from each type, and will repeat this in the shop view too.
is there a way to make all this types in one model without putting all fields in one Model and make it

blank = True, null = True

i thought i can make each type as a main category with no parent but i still have this issue for the fields how to make it.

right now i made it with two models in models.py
but i will query them as two seprate objects lists in views.py or Maybe is there a way i can query all in one List Object?

from django.db import models
from django.urls import reverse


def default_image():
	return 'default/default.png'


def uploaded_category_image(self, filename):
	return f'Categories/{self.name}/{"Category_image.png"}'


class Category(models.Model):	
	name = models.CharField(verbose_name='Name', max_length=255, unique=True)
	slug = models.SlugField(verbose_name='Slug', unique=True)
	description = models.TextField(verbose_name='Description', blank=True, null=True)
	age = models.IntegerField(verbose_name='Age', null=True, blank=True)
	parent = models.ForeignKey('self', blank=True, null=True, on_delete=models.SET_NULL)
	image = models.ImageField(verbose_name='Image', upload_to=uploaded_category_image, default=default_image, blank=True, null=True)

	class Meta:
		verbose_name_plural = 'Categories'
		ordering = ['name',]

	def __str__(self):
		return self.name


def uploaded_item_image(self, filename):
	return f'Items/{self.sku}/{"EducationalToy_image.png"}'


class EducatioalToy(models.Model):
	item_choices = (
		('sale', 'sale'),
		('featured', 'Featured'),
		('oos', 'Out Of Stock'),
		('new', 'New')
	)
	category = models.ForeignKey('Category', verbose_name='Category', on_delete=models.CASCADE)
	name = models.CharField(verbose_name='Name', max_length=255)
	sku = models.IntegerField(verbose_name='SKU', unique=True)
	slug = models.SlugField(verbose_name='Slug', unique=True)
	description = models.TextField(verbose_name='Description')
	barcode = models.IntegerField(verbose_name='Barcode')
	weight = models.IntegerField(verbose_name='Weight')
	height = models.IntegerField(verbose_name='Height')
	length = models.IntegerField(verbose_name='Length')
	width = models.IntegerField(verbose_name='Width')
	stock = models.IntegerField(verbose_name='Stock')
	price = models.FloatField(verbose_name='Price',)
	age = models.IntegerField(verbose_name='Age')
	tag = models.CharField(verbose_name='Tag', max_length=20, choices=item_choices, blank=True, null=True)
	image1 = models.ImageField(verbose_name='Image1', upload_to=uploaded_item_image, default=default_image, blank=True, null=True)
	image2 = models.ImageField(verbose_name='Image2', upload_to=uploaded_item_image, default=default_image, blank=True, null=True)
	image3 = models.ImageField(verbose_name='Image3', upload_to=uploaded_item_image, default=default_image, blank=True, null=True)
	image4 = models.ImageField(verbose_name='Image4', upload_to=uploaded_item_image, default=default_image, blank=True, null=True)
	image5 = models.ImageField(verbose_name='Image5', upload_to=uploaded_item_image, default=default_image, blank=True, null=True)
	image6 = models.ImageField(verbose_name='Image6', upload_to=uploaded_item_image, default=default_image, blank=True, null=True)
	image7 = models.ImageField(verbose_name='Image7', upload_to=uploaded_item_image, default=default_image, blank=True, null=True)

	def get_item_dimension(self):
		dimension = f'{self.length} X {self.width} X {self.height}'
		return dimension

	def item_has_stock(self):
		if self.stock <= 0:
			return False
		return True

	def get_absolute_url(self):
		return reverse('item_detail', kwargs={'slug' : self.slug})

	class Meta:
		ordering = ['category', 'name', 'sku']


class Furniture(models.Model):

	colors_choices = (('woody', 'Woody'), ('beige', 'Beige'), ('blue', 'Blue'), ('green', 'Green'), ('yellow', 'Yellow'), ('orange', 'Orange'), ('red', 'Red'),
		('natural plywood', 'Natural Plywood'), ('gray', 'Gray'), ('lightblue', 'Lightblue'), ('brown', 'Brown'))
	
	sku_choices = (
		('11', '11'), ('12', '12'), ('15', '15'), ('16', '16'), ('19', '19'), ('17', '17'), ('18', '18'), ('01', '01'), ('05', '05'), ('06', '06'), ('09', '09'), ('07', '07'), ('08', '08'), ('32', '32'), ('33', '33'),
		('35', '35'), ('34', '34'), ('36', '36'), ('39', '39'), ('37', '37'), ('38', '38'), ('42', '42'), ('43', '43'), ('45', '45'), ('46', '46'), ('49', '49'), ('47', '47'), ('48', '48'), ('52', '52'),
		('54', '54'), ('56', '56'), ('59', '59'), ('57', '57'), ('58', '58'), ('60', '60'))

	material_choices = (('hpl', 'HPL Melanine'), ('birch plywood', 'Birch Plywood'), ('shell', 'Shell'), ('metal frame', 'Metal Frame'), ('leather', 'Leather'))

	age_symbol_choices = (('c02', 'C02'), ('c01', 'C01'), ('c0', 'C0'), ('c1', 'C1'), ('c2', 'C2'), ('c3', 'C3'), ('c4', 'C4'))

	item_choices = (('sale', 'sale'), ('featured', 'Featured'), ('oos', 'Out Of Stock'), ('new', 'New'))

	category = models.ForeignKey('Category', verbose_name='Category', on_delete=models.CASCADE)
	name = models.CharField(verbose_name='Name', max_length=255)
	sku = models.IntegerField(verbose_name='SKU', unique=True)
	description = models.TextField(verbose_name='Description')
	barcode = models.IntegerField(verbose_name='Barcode')
	weight = models.IntegerField(verbose_name='Weight')
	height = models.IntegerField(verbose_name='Height')
	length = models.IntegerField(verbose_name='Length')
	width = models.IntegerField(verbose_name='Width')
	stock = models.IntegerField(verbose_name='Stock')
	price = models.FloatField(verbose_name='Price',)
	age = models.IntegerField(verbose_name='Age')
	age_symbol = models.CharField(verbose_name='Age Symbol', max_length=255, choices=age_symbol_choices)
	tag = models.CharField(verbose_name='Tag', max_length=20, choices=item_choices, blank=True, null=True)
	frame_sku = models.CharField(verbose_name='Frame SKU', max_length=2, choices=sku_choices)
	frame_color = models.CharField(verbose_name='Frame Color', max_length=255, choices=colors_choices)
	frame_material = models.CharField(verbose_name='Frame Material', max_length=255, choices=material_choices)
	top_seat_sku = models.CharField(verbose_name='Top Or Seat-Back SKU', max_length=2, choices=sku_choices)
	top_seat_color = models.CharField(verbose_name='Top Or Seat-Back Color', max_length=255, choices=colors_choices)
	top_seat_material = models.CharField(verbose_name='Top Or Seat-Back Material', max_length=255, choices=material_choices)
	slug = models.SlugField(verbose_name='Slug', unique=True)
	image1 = models.ImageField(verbose_name='Image1', upload_to=uploaded_item_image, default=default_image)
	image2 = models.ImageField(verbose_name='Image2', upload_to=uploaded_item_image, default=default_image)
	image3 = models.ImageField(verbose_name='Image3', upload_to=uploaded_item_image, default=default_image)
	image4 = models.ImageField(verbose_name='Image4', upload_to=uploaded_item_image, default=default_image)
	image5 = models.ImageField(verbose_name='Image5', upload_to=uploaded_item_image, default=default_image)
	image6 = models.ImageField(verbose_name='Image6', upload_to=uploaded_item_image, default=default_image)
	image7 = models.ImageField(verbose_name='Image7', upload_to=uploaded_item_image, default=default_image)

	class Meta:
		ordering = ['category', 'name', 'sku']

	def get_item_dimension(self):
		dimension = f'{self.length} X {self.width} X {self.height}'
		return dimension

	def item_has_stock(self):
		if self.stock <= 0:
			return False
		return True

	def get_absolute_url(self):
		return reverse('item_detail', kwargs={'slug' : self.slug})

There are a couple different ways to model this in the database. But the way I typically recommend as a starting point is to use what Django refers to as Multi-Table inheritance.

You’ll have a table with all the columns in common, and then create other tables that inherit from that common table, adding the fields for each sub-table.

Thanks Ken very much,
This way is cool and i will be able to query Items All Including all types of Child Classes.
this is how i do it as reference for future.

def uploaded_item_image(self, filename):
	return f'Items/{self.sku}/{"EducationalToy_image.png"}'


class Item(models.Model):

	item_choices = ( ('sale', 'sale'), ('featured', 'Featured'), ('out of stock', 'Out Of Stock'), ('new', 'New'), ('best seller', 'Best Seller') )

	category = models.ForeignKey('Category', verbose_name='Category', on_delete=models.CASCADE)
	name = models.CharField(verbose_name='Name', max_length=255)
	description = models.TextField(verbose_name='Description')
	barcode = models.IntegerField(verbose_name='Barcode')
	weight = models.IntegerField(verbose_name='Weight')
	height = models.IntegerField(verbose_name='Height')
	length = models.IntegerField(verbose_name='Length')
	width = models.IntegerField(verbose_name='Width')
	stock = models.IntegerField(verbose_name='Stock')
	price = models.FloatField(verbose_name='Price',)
	age = models.IntegerField(verbose_name='Age')
	tag = models.CharField(verbose_name='Tag', max_length=20, choices=item_choices, blank=True, null=True)
	image1 = models.ImageField(verbose_name='Image1', upload_to=uploaded_item_image, default=default_image, blank=True, null=True)
	image2 = models.ImageField(verbose_name='Image2', upload_to=uploaded_item_image, default=default_image, blank=True, null=True)
	image3 = models.ImageField(verbose_name='Image3', upload_to=uploaded_item_image, default=default_image, blank=True, null=True)
	image4 = models.ImageField(verbose_name='Image4', upload_to=uploaded_item_image, default=default_image, blank=True, null=True)
	image5 = models.ImageField(verbose_name='Image5', upload_to=uploaded_item_image, default=default_image, blank=True, null=True)
	image6 = models.ImageField(verbose_name='Image6', upload_to=uploaded_item_image, default=default_image, blank=True, null=True)
	image7 = models.ImageField(verbose_name='Image7', upload_to=uploaded_item_image, default=default_image, blank=True, null=True)

	def get_item_dimension(self):
		dimension = f'{self.length} X {self.width} X {self.height}'
		return dimension

	def item_has_stock(self):
		if self.stock <= 0:
			return False
		return True

	def get_absolute_url(self):
		return reverse('item_detail', kwargs={'slug' : self.slug})

	class Meta:
		ordering = ['category', 'name', 'sku']


class EducatioalToy(Item):
	toy = models.OneToOneField(Item, verbose_name='Toy', on_delete=models.CASCADE, parent_link=True)
	sku = models.CharField(verbose_name='SKU', unique=True, max_length=20)
	slug = models.SlugField(verbose_name='Slug', unique=True)


class Chair(Item):
	
	# Choices Lists.
	colors_choices = ( ('woody', 'Woody'), ('beige', 'Beige'), ('blue', 'Blue'), ('green', 'Green'), ('yellow', 'Yellow'), ('orange', 'Orange'), ('red', 'Red'),
		('natural plywood', 'Natural Plywood'), ('gray', 'Gray'), ('lightblue', 'Lightblue'), ('brown', 'Brown') )
	
	sku_choices = ( ('11', '11'), ('12', '12'), ('15', '15'), ('16', '16'), ('19', '19'), ('17', '17'), ('18', '18'), ('01', '01'), ('05', '05'), ('06', '06'), ('09', '09'), ('07', '07'), ('08', '08'), ('32', '32'),
		('33', '33'), ('35', '35'), ('34', '34'), ('36', '36'), ('39', '39'), ('37', '37'), ('38', '38'), ('42', '42'), ('43', '43'), ('45', '45'), ('46', '46'), ('49', '49'), ('47', '47'), ('48', '48'),
		('52', '52'), ('54', '54'), ('56', '56'), ('59', '59'), ('57', '57'), ('58', '58'), ('60', '60') )

	material_choices = ( ('hpl', 'HPL Melanine'), ('birch plywood', 'Birch Plywood'), ('shell', 'Shell'), ('metal frame', 'Metal Frame'), ('leather', 'Leather') )

	age_symbol_choices = ( ('c02', 'C02'), ('c01', 'C01'), ('c0', 'C0'), ('c1', 'C1'), ('c2', 'C2'), ('c3', 'C3'), ('c4', 'C4') )

	item_choices = ( ('sale', 'sale'), ('featured', 'Featured'), ('oos', 'Out Of Stock'), ('new', 'New') )

	# Fields
	chair = models.OneToOneField(Item, verbose_name='Chair', on_delete=models.CASCADE, parent_link=True)
	weight = models.IntegerField(verbose_name='Weight')
	height = models.IntegerField(verbose_name='Height')
	length = models.IntegerField(verbose_name='Length')
	width = models.IntegerField(verbose_name='Width')
	age_symbol = models.CharField(verbose_name='Age Symbol', max_length=255, choices=age_symbol_choices)
	frame_sku = models.CharField(verbose_name='Frame SKU', max_length=2, choices=sku_choices)
	frame_color = models.CharField(verbose_name='Frame Color', max_length=255, choices=colors_choices)
	frame_material = models.CharField(verbose_name='Frame Material', max_length=255, choices=material_choices)
	top_seat_sku = models.CharField(verbose_name='Top Or Seat-Back SKU', max_length=2, choices=sku_choices)
	top_seat_color = models.CharField(verbose_name='Top Or Seat-Back Color', max_length=255, choices=colors_choices)
	top_seat_material = models.CharField(verbose_name='Top Or Seat-Back Material', max_length=255, choices=material_choices)
	_sku = models.CharField(verbose_name='SKU', unique=True, max_length=20)
	slug = models.SlugField(verbose_name='Slug', unique=True)

	# Function To Set SKU, It Is Consists of 3 Parts (sku - frame_sku - top_seat_sku) Concatenated.
	@property
	def _sku(self):
		return self._sku

	@_sku.setter
	def sku(self):
		'''
		Function To Set SKU, It Is Consists of 3 Parts (sku - frame_sku - top_seat_sku) Concatenated.
		'''
		sku = f'{self.sku}+{self.frame_sku}-{self.top_seat_sku}'
		return sku

Since the topic here is data modeling, I’ll mention that this:

is an anti-pattern. This type of situation is generally more appropriately modeled by having a separate model with a ForeignKey to the Category table. That allows you to have an indeterminate number of images referring to a Category, avoid the null fields, and provide for easier manipulations of those images relative to the category such as the ability to more easily reorder those images (if the sequence is important), and reassign images between Category instances.