Django model constraint or business logic - python-3.x

I am building my first django project, it will essentially be an assessment form. Each question will be multiple choice. There is no right or wrong answer the questions but instead a grade of what level they are at. So for each question a user can only select one answer out of the possible three options.
I have defined the following models
from django.db import models
# Create your models here.
class Category(models.Model):
name = models.CharField(max_length=100)
def __str__(self):
return self.name
class Meta:
verbose_name_plural = 'Categories'
class Question(models.Model):
category = models.ForeignKey(Category, on_delete=models.PROTECT)
capability = models.TextField()
weight = models.FloatField(default=1.0)
def __str__(self):
return self.capability
class Grade(models.Model):
name = models.CharField(max_length=100)
score = models.PositiveIntegerField()
def __str__(self):
return self.name
class Answer(models.Model):
question = models.ForeignKey(Question, on_delete=models.PROTECT)
grade = models.ForeignKey(Grade, on_delete=models.PROTECT)
description = models.TextField()
def __str__(self):
return f'Q{self.question.id} - {self.description}'
class Area(models.Model):
name = models.CharField(max_length=100)
def __str__(self):
return self.name
class Assessment(models.Model):
created = models.DateTimeField(auto_now_add=True)
area = models.ForeignKey(Area, on_delete=models.PROTECT)
answers = models.ManyToManyField(Answer)
def __str__(self):
return f'{self.area} - {self.created}'
While an assessment will have multiple answers, it should only be able to contain one answer per question. Can this type of constraint be designed in the model? or it would be part of the business logic. Esentially the user will pick an answer in the webpage from a radio input so will only be able to select one. The admin page will allow me to select multiple answers form the same question.
I am just trying to find out if there is an elegant way to design this within the model, or just use the business logic to ensure only one answer per question is allowed.

Related

How change a function based view into a class based?

I want to write a category detail view in order to do so i want to change this function based view
def CategoryView(request, cats):
category_posts = Post.objects.filter(category=cats.replace('-', ' '))
return render(request, 'categories.html', {'cats':cats.replace('-', ' ').title(), 'category_posts':category_posts})
into the class based view. My first question: 1. How to do so?; 2.How also change the url for the view?;
path('category/<str:cats>/', CategoryView, name='category'),
Here is my models:
from django.conf import settings
from django.db import models
from django.urls import reverse
class Category(models.Model):
name = models.CharField(max_length=255)
def __str__(self):
return (self.name)
def get_absolute_url(self):
return reverse("home")
class Post(models.Model):
title = models.CharField(max_length=255)
body = models.TextField()
author = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE,)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
category = models.ManyToManyField(Category, related_name='categories')
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse("post_detail", kwargs={"pk": self.pk})
#property
def categories(self):
return ', '.join([x.name for x in self.category.all()])
class Comment(models.Model):
article = models.ForeignKey(Post, null=True, blank=True, on_delete=models.CASCADE)
comment = models.CharField(max_length=140)
author = models.ForeignKey(settings.AUTH_USER_MODEL,on_delete=models.CASCADE,)
def __str__(self):
return self.comment
def get_absolute_url(self):
return reverse("post_list")
When you write an answer, if you don't mind, can you also write an explanation a step by step. And can you also write how did you figure out the answer. A lost a useful materials would be also helpful. Thank you in advance
You url shall look like :
path('category/<str:cats>/', CategoryClassView.as_view(), name='category')
Since in your case, you want to render 'html', below is example class view
#in your views.py
from django.views.generic import TemplateView
class CategoryClassView(TemplateView):
template_name = "categories.html"
def get_context_data(self, **kwargs):
cats = kwargs.get("cats")
category_posts = Post.objects.filter(category=cats.replace('-', ' '))
context = {'cats':cats.replace('-', ' ').title(), 'category_posts':category_posts}
return context
To answer your second part regarding how did I figure it out. Most of the the logic you have written in your function based view would be used as it, only thing you need to figure out apt attribute and method to overide. Checkout basic concept of python-inheritance and just figure out the attributes/method of particular class you want to overide. Documentation is always a good start for it.

Django search with AND operator many to many relationship

This is my model
class MenuItem(models.Model):
name = models.CharField(max_length=500, null=False)
description = models.CharField(max_length=500, null=True)
image_url = models.CharField(max_length=1000, null=True)
menu_category = models.ForeignKey(MenuCategory, on_delete=models.CASCADE)
def __str__(self):
return f'{self.name}'
class Venue(models.Model):
name = models.CharField(max_length=500, null=False)
def __str__(self):
return f'{self.name}'
class VenueMenu(models.Model):
venue = models.ForeignKey(Venue, null=False, on_delete=models.CASCADE)
menu_item = models.ManyToManyField(MenuItem, null=False)
This is my view
#api_view(['GET'])
def search_menu_item(request):
if request.GET.get('venue') and request.GET.get('search_name'):
menu_item_filter = Q(menu_item__name__icontains=request.GET.get('search_name'))
venue_filter = Q(venue__name=request.GET.get('venue').title())
menu_item_search = VenueMenu.objects.filter(venue_filter & menu_item_filter)
serializer = VenueMenuSerializer(menu_item_search, many=True)
return Response(serializer.data)
This is my serializer
class MenuItemSerializer(serializers.ModelSerializer):
menu_category = MenuCategorySerializer(many=True)
class Meta:
model = MenuItem
fields = '__all__'
class VenueSerializer(serializers.ModelSerializer):
class Meta:
model = Venue
fields = '__all__'
class VenueMenuSerializer(serializers.ModelSerializer):
menu_item = MenuItemSerializer(many=True)
venue = VenueSerializer(many=False)
I want to search a single menu item information in a specific venue, as you can see in my query am querying the object but this query is returning me all the menu items associated with that venue including the one which I have searched but I want to have a single menu item which I am searching associated to that venue.
You get the correctly filtered VenueMenus, but the related MenuItems aren't filtered automatically because the filter is for VenueMenu and not MenuItem.
To filter the related MenuItems in the serializer, you'll have to do a filtered prefetch using Prefetch like so:
from django.db.models import Prefetch
menu_item_search = VenueMenu.objects.filter(
venue_filter & menu_item_filter
).prefetch_related(
Prefetch(
'menu_item',
queryset=MenuItem.objects.filter(name__icontains=request.GET.get('search_name'))
)
)
Also to improve this further, you can also select the related Venue using select_related to avoid doing a separate query just to get the venue details in the serializer. So all in all:
menu_item_search = VenueMenu.objects.filter(
venue_filter & menu_item_filter,
).select_related(
'venue',
).prefetch_related(
Prefetch(
'menu_item',
queryset=MenuItem.objects.filter(name__icontains=request.GET.get('search_name'))
)
)

Access manyTomany field (Label) from category class via subclass(Products) Category-> Products -> Labels

Here is the code of my models file:
from django.db import models
# Create your models here.
class Category(models.Model):
name = models.CharField(max_length=255)
def __str__(self):
return self.name
class Product(models.Model):
category = models.ForeignKey(Category, on_delete=models.CASCADE, related_name='products')
name = models.CharField(max_length=255)
price = models.DecimalField(decimal_places=2, max_digits=9)
def __str__(self):
return self.name
class Label(models.Model):
name = models.CharField(max_length=255)
products = models.ManyToManyField(Product, related_name='labels')
def __str__(self):
return self.name
Now I want to access manyTomany field i.e. Label from Category
please help me
Thanks in advance
You can try like this.
# first get the single category object
category = Category.objects.get(pk=1)
# querying products in this category
products = category.products.all()
# now filter the Label model based on the category products
labels = Label.objects.filter(products__in=products)

How to make ForeignKey field in a separate table without creating an app

This is more of a curiosity question.
Is there a way I could make a ForeignKey field with a table below?
I know I could separate the models into separate app but apart froom creating seperate models file.
from account.models import User
class Department(models.Model):
**department_head = models.ForeignKey(Employee, null=True, blank=True, on_delete=models.CASCADE)**
name = models.CharField(max_length=200)
description = models.TextField(null=True)
class Meta:
ordering = ('name',)
def __str__(self):
return self.name
class Employee(User):
full_name = models.CharField(max_length=200, default='')
gender = models.CharField(choices=GENDER_CHOICES, max_length=200)
department = models.ForeignKey(Department,on_delete=models.CASCADE)
I expect to make a foreignkey relationship bw department and employee but don't want to create a seperate file or app.

How to access/override defaults on parent's attribute in SQL Alchemy?

I have the following classes: Item, StoneSword, IronSword, Apple, Steak
Item is the parent class, and the Swords and food inherit the Item class. I want to have a bunch of different functions and descriptions for each of these classes.
I completely understand the function side of things, but I am struggling to figure out how to override the default description with the children classes.
I am currently using single table inheritance with SQLAlchemy.
Credit goes to Mike Bayer for helping me figure out how to override __init__() to suit my needs. If anybody can find a more elegant solution, I will be glad to accept that answer instead.
class User(Base):
__tablename__ = 'users'
id = Column(Integer, primary_key=True)
name = Column(String, default="User")
fullname = Column(String)
email = Column(String)
type = Column(String)
__mapper_args__ = {
'polymorphic_on': type,
'polymorphic_identity': 'user'
}
def __init__(self, **kwargs):
super().__init__(**kwargs)
def __repr__(self):
return f"<User(id={self.id}, name='{self.name}', fullname='{self.fullname}', email='{self.email}')>"
def is_admin(self):
print(f"I, {self.name}, am NOT an admin")
class Admin(User):
def __init__(self, **kwargs):
kwargs.setdefault("name", "Admin")
super().__init__(**kwargs)
__mapper_args__ = {
'polymorphic_identity': 'admin'
}

Resources