Надлежащая реализация архитектуры наследования модели Django? - PullRequest
3 голосов
/ 09 октября 2019

Я создаю простое приложение Django для обзоров различных объектов (ресторанов, автосервисов, автомоек и т. Д.).

Я начал с приложения, но вскоре столкнулся с первой проблемой. Каждый объект имеет особенности (но каждый тип объекта имеет различные особенности).

Например:

  • В ресторанах есть сад, детская площадка, места, тип кухни и т. Д.
  • автомойки имеют внешнюю очистку, внутреннюю очистку и т. Д.

Итак, я начал строить типичную реализацию БД с таблицами ManyToMany, но затем нашел Django Model Inheritance, поэтому я реализовал его вмое приложение, как вы можете видеть:

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)

Все было хорошо, пока мне не пришлось показывать объекты для каждой категории, как здесь (я использую рестораны вместо ресторана, потому что URL выглядит оченьлучше):

  <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>

Вы можете проверить функцию show_all_objects, поэтому я сделал это с помощью eval():

categories = {'restaurants' : 'Restaurant', 'sportfitness' : 'SportFitness', 'carservice' : 'CarService', 'beautysalon' : 'BeautySalon', 'fastfood' : 'FastFood', 'carwash' : 'CarWash', 'fun' : 'Fun', 'other' : 'Other'}
objects = eval(categories[category]).objects.all()

Все было хорошо, но потом я снова столкнулся с этой проблемой. Когда я хочу показать объект с его функциями в show_object() -методе (вы можете проверить это в коде выше), я могу получить объект, но не могу получить, например, restaurant или car wash и т. Д .:

def show_object(request, pk):
    obj = Object.objects.get(id=5)

Теперь у меня есть объект, но я не могу получить точный объект. Я снова сталкиваюсь с той же проблемой, когда хочу показать разные формы для каждого типа объекта (например, рестораны должны иметь форму с флажками со своими функциями, форму для автомойки с флажками для ее функций и т. Д.).

PS: Основываясь на ответе @ Saawhat , он рекомендует мне использовать функцию eval() в show_object(), но я передаю все объекты в шаблоне, поэтому я не могу передать ему параметр типа param:

<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>

В этой строке:

<a href="{% url 'show-object' category='' pk=object.id%}"

Я не могу пройти категорию, потому что у меня ее нет

Ответы [ 2 ]

5 голосов
/ 11 октября 2019

1: при использовании наследования для моделей укажите

class Meta:
    abstract = True

в базовой модели, что в вашем случае равно Object, чтобы он не создавал свой собственный экземпляр / таблицу. Таким образом, в этом случае ваша модель будет выглядеть как

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)

    class Meta:
        abstract = True

    def __str__(self):
        return f"{self.title}"

На другой ноте переименуйте Object во что-то еще, например BaseModel или что-то еще.

2: когда вы звоните, делая show_object передать категорию / имя модели и запрос, например:

obj = Object.objects.get(id=5) #Instead of this
obj = eval(categories[category]).objects.get(pk=pk) # Do this

3: Для вашей модели Comment либо вы добавляете связку внешних ключей для каждоговашей модели или вы можете использовать Generic Relation в Django .

4: при показе форм используйте соответствующие модели, а не Object модель. В любом случае у вас есть доступ ко всем полям, потому что вы наследуете от них.

Я надеюсь, что я ответил на все ваши вопросы, если не сообщите мне.

Обновление: Относительно вас не имея возможности передать категорию из шаблона , в ваших представлениях передайте категорию также в шаблон

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,
      'category': category
    }

    return render(request, 'show_all_objects.html', context)

Теперь вы можете получить доступ к category в файле шаблона и передать его обратно.

1 голос
/ 18 октября 2019

Рассмотрите возможность сохранения типа объекта в базе данных, в вашей объектной модели добавьте внешний ключ к ContentType.

from django.contrib.contenttypes.models import ContentType

class Object(models.Model):

    # optional, but this design would also work for abstract inheritance
    class Meta:
        abstract = True

    # ...
    content_type = models.ForeignKey(ContentType)

    def save(*args, **kwargs):
        self.content_type = ContentType.objects.get_for_model(self.__class__)
        super().save(*args, **kwargs)

Затем в шаблоне:

<a href="{% url 'show-object' category=object.content_type.model pk=object.id %}">

ВыЗатем можно найти вашу модель в ваших представлениях, например, так:

def show_object(request, category, pk):
    # swap out `myapp` to your app name
    # if you don't care about url being friendly
    # you can use content type primary key instead of model name
    model = ContentType.objects.get(app_label="myapp", model=category).model_class()
    obj = model.objects.get(pk=pk)
    # ...

def show_all_objects(request, category):
    model = ContentType.objects.get(app_label="myapp", model=category).model_class()
    all_objects = model.objects.all()
    # ...
...