Mysql
 sql >> Baza danych >  >> RDS >> Mysql

Django views.py Wersja SQL Join z Multi Table Query

Cóż, są to niejasne nazwy tabel i pól, ale najlepiej mogę powiedzieć, że zapytanie wyglądałoby mniej więcej tak:

(Restaurant.objects.filter(city=8, 
     cuisine__cuisinetype__cuisine="Italian").distinct().order_by('name')[:20])

Ale jeśli nie jesteś zablokowany w tym schemacie bazy danych, Twoje modele będą wyglądać lepiej, jak:

class CuisineType(models.Model):
    name = models.CharField(max_length=50)
    class Meta:
        db_table = 'cuisinetype'

class Restaurants(models.Model):
    city = models.ForeignKey("City", null=True, blank=True) # Apparently defined elsewhere. Should be part of location?
    name = models.CharField(max_length=50)
    location = models.ForeignKey("Location", null=True, blank=True) # Apparently defined elsewhere.
    cuisines = models.ManyToManyField(CuisineType)

Wtedy zapytanie będzie bardziej przypominać:

Restaurant.objects.filter(city=8, cuisines__name="Italian").order_by('name')[:20]

OK, przejdźmy przez Twoje zapytanie, zakładając brak zmian w kodzie. Zaczniemy od podzapytania.

SELECT DISTINCT res_id FROM cuisine 
        JOIN    cuisinetype ON cuisine.cuisineid = cuisinetype.`cuisineid`
        WHERE   cuisinetype.`cuisine` = 'Italian'

Patrzymy na klauzulę WHERE i widzimy, że potrzebujemy JOIN. Aby wykonać złączenie, musisz zadeklarować pole relacyjne w jednym z łączonych modeli (Django doda relację odwrotną, którą powinniśmy nazwać). Więc dopasowujemy cuisine.cuisineid z `typem kuchni.cuisineid. To okropne nazewnictwo.

To jest relacja wiele-do-wielu, więc potrzebujemy ManyToManyField . Cóż, patrząc na Cuisine model, to naprawdę stół łączący dla tego M2M. Django oczekuje, że stół łączący będzie miał dwa ForeignKey pola, jeden skierowany na każdą stronę stawu. Normalnie stworzy to dla ciebie, aby zachować zdrowie psychiczne. Najwyraźniej nie masz tyle szczęścia. Musisz więc ręcznie go podłączyć.

Wygląda na to, że pole „GID” jest (bezużytecznym) polem identyfikatora rekordu, więc załóżmy, że jest to liczba całkowita zwiększająca się automatycznie. (Aby się upewnić, sprawdź polecenia CREATE TABLE.) Teraz możemy przepisać Cuisine modelować w coś zbliżonego do zdrowego rozsądku:

class Cuisine(models.Model):
    cuisinegid = models.AutoField(primary_key=True, db_column='CuisineGID')
    cuisineid = models.ForeignKey("Cuisinetype", null=True, 
        db_column='CuisineID', blank=True)
    res_id = models.ForeignKey("Restaurant", null=True, db_column='Res_ID', 
        blank=True)
    class Meta:
        db_table = 'cuisine'

Nazwy modeli są cytowane, ponieważ modele nie zostały jeszcze zdefiniowane (znajdują się później w pliku). Teraz nie ma wymogu, aby nazwy pól Django pasowały do ​​nazw kolumn, więc zmieńmy je na bardziej czytelne. Pole identyfikatora rekordu ma zwykle nazwę id , a klucze obce są zwykle nazywane według tego, do czego się odnoszą:

class Cuisine(models.Model):
    id = models.AutoField(primary_key=True, db_column='CuisineGID')
    cuisine_type = models.ForeignKey("CuisineType", null=True, 
        db_column='CuisineID', blank=True)
    restaurant = models.ForeignKey("Restaurant", null=True, db_column='Res_ID', 
        blank=True)
    class Meta:
        db_table = 'cuisine'

OK, skończyliśmy definiować nasz wspólny stół. Skoro już przy tym jesteśmy, zastosujmy to samo do naszego Cuisinetype Model. Zwróć uwagę na poprawioną nazwę klasy wielbłąda:

class CuisineType(models.Model):
    id = models.AutoField(primary_key=True, db_column='CuisineID')
    name = models.CharField(max_length=50, db_column='Cuisine', blank=True)
    class Meta:
        db_table = 'cuisinetype'

Więc w końcu dotarliśmy do naszej Restaurant Model. Zwróć uwagę, że nazwa jest liczba pojedyncza; obiekt reprezentuje tylko jeden rekord.

Zauważyłem, że brakuje w nim jakiejkolwiek dp_table lub db_column rzeczy, więc wychodzę na kończynę i zgaduję, że to Django to tworzy. Oznacza to, że możemy pozwolić mu utworzyć id pole dla nas i możemy je pominąć w naszym kodzie. (Jeśli tak nie jest, to po prostu dodajemy to tak, jak w przypadku innych modeli. Ale naprawdę nie powinieneś mieć identyfikatora rekordu dopuszczającego wartość null.) I tutaj nasza kuchnia wpisuje ManyToManyField mieszka:

class Restaurants(models.Model):
    city_id = models.ForeignKey(null=True, blank=True)
    name = models.CharField(max_length=50, blank=True)
    location = models.ForeignKey(null=True, blank=True)
    cuisine_types = models.ManyToManyField(CuisineType, through=Cuisine,
        null=True, blank=True)

Zwróć uwagę, że nazwa pola M2M jest w liczbie mnogiej, ponieważ ta relacja prowadzi do wielu rekordów.

Jeszcze jedną rzeczą, którą będziemy chcieli dodać do tego modelu, są nazwy relacji odwrotnych. Innymi słowy, jak przejść z innych modeli z powrotem do Restaurant . Robimy to, dodając related_name parametry. Nie jest niczym niezwykłym, że są takie same.

class Restaurant(models.Model):
    city_id = models.ForeignKey(null=True, blank=True, 
        related_name="restaurants")
    name = models.CharField(max_length=50, blank=True)
    location = models.ForeignKey(null=True, blank=True, 
        related_name="restaurants")
    cuisine_types = models.ManyToManyField(CuisineType, through=Cuisine,
        null=True, blank=True, related_name="restaurants")

Teraz w końcu jesteśmy gotowi. Spójrzmy więc na Twoje zapytanie:

SELECT  restaurants.`name`, restaurants.`address`, cuisinetype.`cuisine`
FROM    restaurants
JOIN    cuisinetype ON cuisinetype.cuisineid = restaurants.`cuisine`
WHERE   city_id = 8 AND restaurants.id IN (
        SELECT DISTINCT res_id FROM cuisine 
        JOIN    cuisinetype ON cuisine.cuisineid = cuisinetype.`cuisineid`
        WHERE   cuisinetype.`cuisine` = 'Italian')
ORDER BY restaurants.`name`
LIMIT 20

Ponieważ to jest FROM restaurants , zaczniemy od domyślnego menedżera obiektów tego modelu, objects :

Restaurant.objects

WHERE w tym przypadku klauzula to filter() zadzwoń, więc dodajemy go na pierwszy semestr:

Restaurant.objects.filter(city=8)

Możesz mieć więdną wartość klucza głównego lub City obiekt po prawej stronie tego terminu. Reszta zapytania staje się jednak bardziej złożona, ponieważ wymaga JOIN . Złączenie w Django wygląda jak dereferencja poprzez pole relacji. W zapytaniu oznacza to połączenie odpowiednich nazw pól z podwójnym podkreśleniem:

Restaurant.objects.filter(city=8, cuisine_type__name="Italian")

Django wie, do których pól należy dołączyć, ponieważ jest to zadeklarowane w Cuisine tabela, która jest pobierana przez through=Cuisine parametr w cuisine_types . wie również, jak wykonać podzapytanie, ponieważ przechodzisz przez relację M2M.

To daje nam SQL odpowiednik:

SELECT  restaurants.`name`, restaurants.`address`
FROM    restaurants
WHERE   city_id = 8 AND restaurants.id IN (
        SELECT res_id FROM cuisine 
        JOIN    cuisinetype ON cuisine.cuisineid = cuisinetype.`cuisineid`
        WHERE   cuisinetype.`cuisine` = 'Italian')

W połowie drogi. Teraz potrzebujemy SELECT DISTINCT więc nie otrzymujemy wielu kopii tego samego rekordu:

Restaurant.objects.filter(city=8, cuisine_type__name="Italian").distinct()

I musisz pokazać rodzaje kuchni, aby je wyświetlić. Okazuje się, że zapytanie, które masz, jest tam nieefektywne, ponieważ prowadzi tylko do tabeli sprzężenia i musisz uruchomić dalsze zapytania, aby uzyskać powiązany CuisineType dokumentacja. Zgadnij co:Django Cię obejmuje.

(Restaurant.objects.filter(city=8, cuisine_type__name="Italian").distinct()
    .prefetch_related("cuisine_types"))

Django uruchomi dwa zapytania:jedno, takie jak twoje, aby uzyskać wspólne identyfikatory, a drugie, aby uzyskać powiązany CuisineType dokumentacja. Wtedy dostęp poprzez wynik zapytania nie musi wracać do bazy danych.

Dwie ostatnie rzeczy to kolejność:

(Restaurant.objects.filter(city=8, cuisine_type__name="Italian").distinct()
    .prefetch_related("cuisine_types").order_by("name"))

Oraz LIMIT :

(Restaurant.objects.filter(city=8, cuisine_type__name="Italian").distinct()
    .prefetch_related("cuisine_types").order_by("name")[:20])

I oto twoje zapytanie (i powiązane zapytanie) upakowane w dwóch wierszach Pythona. Pamiętaj, że w tym momencie zapytanie nie zostało nawet wykonane. Musisz umieścić go w czymś, na przykład w szablonie, zanim cokolwiek zrobi:

def cuisinesearch(request, cuisine):
    return render_to_response('cuisinesearch.html', {
        'restaurants': (Restaurant.objects.filter(city=8, 
             cuisine_type__name="Italian").distinct()
             .prefetch_related("cuisine_types").order_by("name")[:20])
        })

Szablon:

{% for restaurant in cuisinesearch %}
<h2>{{ restaurant.name }}</h2>
<div class="location">{{ restaurant.location }}</div>
<h3>Cuisines:</h3>
<ul class="cuisines">{% for ct in restaurant.cuisine_types.all %}
<li>{{ ct.name }}</li>{% endfor %}
</ul>
{% endfor %}



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Codeigniter simple_query vs. konstruktor zapytań (wstaw, zaktualizuj i usuń)

  2. Zaktualizuj dane bazy danych za pomocą przycisku przesyłania

  3. Upuścić bazy danych MySQL pasujące do jakiegoś symbolu wieloznacznego?

  4. Skąd mogę pobrać mysql jdbc jar?

  5. Jak porównać dwie tabele w MySQL?