I'm building a simple Django app for reviews of different objects (restaurants, car services, car wash etc).
I started with the app, but soon I faced first problem. Every object has features (but every type of object has different features).
For example:
- restaurants have garden, playground, seats, type of kitchen, etc.
- car washes have external cleaning, internal cleaning etc.
So I started to build a typical DB implementation with ManyToMany tables, but then I found Django Model Inheritance, so I implement it in my APP, as you can see:
urls.py:
from django.urls import path
from . import views
urlpatterns = [
path('user/<int:pk>/', views.UserObjectsView.as_view(), name='user-objects'),
path('add/', views.add_object, name='add-object'),
path('<str:category>/<int:pk>/', views.show_object, name='show-object'),
path('all/<str:category>/', views.show_all_objects, name="show-all-objects"),
]
models.py:
from django.db import models
from users.models import ProfileUser
from django.utils import timezone
# Create your models here.
class Object(models.Model):
author = models.ForeignKey(ProfileUser, on_delete=models.CASCADE)
title = models.CharField(max_length=300)
address = models.CharField(max_length=300)
content = models.TextField()
created_date = models.DateTimeField(default=timezone.now)
approved_object = models.BooleanField(default=False)
admin_seen = models.BooleanField(default=False)
def __str__(self):
return f"{self.title}"
class Restaurant(Object):
seats = models.IntegerField()
bulgarian_kitchen = models.BooleanField(default=False)
italian_kitchen = models.BooleanField(default=False)
french_kitchen = models.BooleanField(default=False)
is_garden = models.BooleanField(default=False)
is_playground = models.BooleanField(default=False)
class SportFitness(Object):
is_fitness_trainer = models.BooleanField(default=False)
class CarService(Object):
is_parts_clients = models.BooleanField(default=False)
class BeautySalon(Object):
is_hair_salon = models.BooleanField(default=False)
is_laser_epilation = models.BooleanField(default=False)
class FastFood(Object):
is_pizza = models.BooleanField(default=False)
is_duner = models.BooleanField(default=False)
is_seats = models.BooleanField(default=False)
class CarWash(Object):
is_external_cleaning = models.BooleanField(default=False)
is_internal_cleaning = models.BooleanField(default=False)
is_engine_cleaning = models.BooleanField(default=False)
class Fun(Object):
is_working_weekend = models.BooleanField(default=False)
is_kids_suitable = models.BooleanField(default=False)
class Other(Object):
is_working_weekend = models.BooleanField(default=False)
class Comment(models.Model):
object = models.ForeignKey(Object, on_delete=models.CASCADE, related_name='comments')
author = models.ForeignKey(ProfileUser, on_delete=models.CASCADE)
content = models.TextField()
rating = models.TextField()
created_date = models.DateTimeField(default=timezone.now)
def __str__(self):
return f"{self.content}"
views.py:
from django.shortcuts import render, redirect
from django.views import generic
from objects.models import Object, ProfileUser, Comment, Restaurant, SportFitness, CarService, BeautySalon, FastFood, CarWash, Fun, Other
from .forms import ObjectForm, CommentForm
from django.contrib import messages
from django.db.models import Avg
import sys, pdb
class AllObjects(generic.ListView):
queryset = Object.objects.all()
template_name = 'show_all_objects.html'
class UserObjectsView(generic.ListView):
template_name = 'user_objects.html'
def get_queryset(self):
user_id = self.kwargs['pk']
return Object.objects.filter(author_id = user_id)
def add_object(request):
if not request.user.is_authenticated:
messages.info(request, 'За да добавите нов Обект, трябва да сте регистриран потребител!')
return redirect('account_login')
form = ObjectForm(request.POST or None)
if form.is_valid():
obj = form.save(commit=False)
obj.author = ProfileUser.objects.get(user=request.user)
obj.save()
messages.success(request, 'Успешно добавихте нов Обект, може да видите вашите обекти във вашия профил!')
return redirect('home')
context = {
'form': form
}
return render(request, "add_object.html", context)
def show_object(request, pk):
obj = Object.objects.get(id=5)
if request.method == 'POST':
user = request.user
author = ProfileUser.objects.get(user=user)
comment = Comment()
comment.object = obj
comment.author = author
comment.content = request.POST.get('content')
comment.rating = request.POST.get('rating')
comment.save()
form = CommentForm()
reviews_count = Comment.objects.filter(object_id=pk).count()
rating = Comment.objects.filter(object_id=pk).aggregate(Avg('rating'))['rating__avg']
context = {
'form': form,
'object': obj,
'reviews_count': reviews_count,
'rating': rating
}
return render(request, "show_object.html", context)
def show_all_objects(request, category):
categories = {'restaurants' : 'Restaurant', 'sportfitness' : 'SportFitness', 'carservice' : 'CarService', 'beautysalon' : 'BeautySalon', 'fastfood' : 'FastFood', 'carwash' : 'CarWash', 'fun' : 'Fun', 'other' : 'Other'}
objects = eval(categories[category]).objects.all()
context = {
'object_list': objects,
}
return render(request, 'show_all_objects.html', context)
Everything was fine until I had to show objects for each category, like this (I'm using restaurants instead of Restaurant, because url looks much better):
<a href="{% url 'show-all-objects' category='restaurants' %}" class="utf_category_small_box_part"> <i class="im im-icon-Chef"></i>
<h4>Ресторантии</h4>
<span>22</span>
</a>
<a href="{% url 'show-all-objects' category='sportfitness' %}" class="utf_category_small_box_part"> <i class="im im-icon-Dumbbell"></i>
<h4>Спортни и фитнес</h4>
<span>15</span>
</a>
You can check show_all_objects function, so I did it with eval():
categories = {'restaurants' : 'Restaurant', 'sportfitness' : 'SportFitness', 'carservice' : 'CarService', 'beautysalon' : 'BeautySalon', 'fastfood' : 'FastFood', 'carwash' : 'CarWash', 'fun' : 'Fun', 'other' : 'Other'}
objects = eval(categories[category]).objects.all()
It was fine, but then I faced this problem again. When I want to show an object with its features in show_object()-method (you can check it in code above), I can get the object, but I cannot get restaurant for example, or car wash etc:
def show_object(request, pk):
obj = Object.objects.get(id=5)
Now I have the object, but I cannot get the exact object. I get the same problem again when I want to show a different form for each type of object (for example restaurants should have a form with checkboxes with its features, carwashes form with checkboxes for its features etc).
P.S: Based on @Saawhat answer, he recommends me to use eval() in show_object() function, but I foreach all ojbects in template, so I cannot pass param like category to it:
<div class="row">
{% for object in object_list %}
<div class="col-lg-12 col-md-12">
<div class="utf_listing_item-container list-layout"> <a href="{% url 'show-object' category='' pk=object.id%}" class="utf_listing_item">
<div class="utf_listing_item-image">
<img src="{% static 'core/images/utf_listing_item-01.jpg' %}" alt="">
<span class="like-icon"></span>
<span class="tag"><i class="im im-icon-Hotel"></i> Hotels</span>
<div class="utf_listing_prige_block utf_half_list">
<span class="utf_meta_listing_price"><i class="fa fa-tag"></i> $25 - $45</span>
<span class="utp_approve_item"><i class="utf_approve_listing"></i></span>
</div>
</div>
<div class="utf_listing_item_content">
<div class="utf_listing_item-inner">
<h3>{{ object.title }}</h3>
<span><i class="sl sl-icon-location"></i> {{ object.address }}</span>
<p>{{ object.content }}</p>
</div>
</div>
</a>
</div>
</div>
{% endfor %}
</div>
This row:
<a href="{% url 'show-object' category='' pk=object.id%}"
I cannot pass category, because I don't have one