sql >> Databáze >  >> RDS >> Mysql

Django views.py Verze SQL Join s Multi Table Query

No, to jsou některé nejasné názvy tabulek a polí, ale nejlépe mohu říci, že dotaz bude vypadat nějak takto:

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

Ale pokud nejste uzamčeni do tohoto databázového schématu, vaše modely by vypadaly lépe jako:

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)

Pak by dotaz byl spíše:

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

Dobře, pojďme si projít váš dotaz, za předpokladu, že se váš kód nezmění. Začneme poddotazem.

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

Podíváme se na klauzuli WHERE a uvidíme, že potřebujeme JOIN. Chcete-li provést spojení, musíte deklarovat relační pole v jednom ze spojených modelů (Django přidá obrácenou relaci, kterou bychom měli pojmenovat). Takže srovnáváme cuisine.cuisineid s `cuisinetype.cuisineid. To je nějaké hrozné pojmenování.

To je vztah many-to-many, takže potřebujeme ManyToManyField . Podívejte se na Cuisine model, je to opravdu spojovací stůl pro tento M2M. Django očekává, že spojovací tabulka bude mít dva ForeignKey pole, jedno směřující na každou stranu spoje. Normálně to pro vás vytvoří, abyste zachránili zdravý rozum. Zřejmě takové štěstí nemáte. Musíte jej tedy připojit ručně.

Zdá se, že pole „GID“ je (neužitečné) pole ID pro záznam, takže předpokládejme, že jde o celé číslo s automatickým přírůstkem. (Pro jistotu zkontrolujte příkazy CREATE TABLE.) Nyní můžeme přepsat Cuisine modelovat do něčeho, co se blíží zdravému:

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'

Názvy modelů jsou uvedeny, protože modely ještě nebyly definovány (jsou v souboru později). Nyní není vyžadováno, aby se názvy polí Django shodovaly s názvy sloupců, takže je změňme na něco čitelnějšího. Pole ID záznamu má obvykle název id a cizí klíče jsou obvykle pojmenovány podle toho, k čemu se vztahují:

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, dokončili jsme definování našeho společného stolu. Když už jsme u toho, použijme totéž na náš Cuisinetype Modelka. Všimněte si opraveného názvu třídy velbloudů:

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'

Tak se konečně dostáváme do naší Restaurant Modelka. Všimněte si, že název je singulární; objekt představuje pouze jeden záznam.

Všiml jsem si, že v něm chybí dp_table nebo db_column věci, takže jdu na úd a hádám, že to vytváří Django. To znamená, že mu můžeme nechat vytvořit id pole pro nás a můžeme jej z našeho kódu vynechat. (Pokud tomu tak není, pak to prostě přidáme jako u ostatních modelů. Ale opravdu byste neměli mít nulovatelné ID záznamu.) A tady je typ naší kuchyně ManyToManyField žije:

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)

Všimněte si, že název pole M2M je množné číslo, protože tento vztah vede k více záznamům.

Ještě jedna věc, kterou budeme chtít k tomuto modelu přidat, jsou názvy pro obrácené vztahy. Jinými slovy, jak přejít z ostatních modelů zpět do Restaurant . To provedeme přidáním related_name parametry. Není neobvyklé, že jsou stejní.

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

Nyní jsme konečně připraveni. Podívejme se tedy na váš dotaz:

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

Protože toto je FROM restaurants , začneme s výchozím správcem objektů tohoto modelu, objects :

Restaurant.objects

WHERE klauzule je v tomto případě filter() zavolejte, tak to přidáme pro první termín:

Restaurant.objects.filter(city=8)

Můžete mít hodnotu primárního klíče nebo City objekt na pravé straně tohoto termínu. Zbytek dotazu je však složitější, protože potřebuje JOIN . Spojení v Django prostě vypadá jako dereferencování přes pole vztahu. V dotazu to znamená spojení příslušných názvů polí dvojitým podtržením:

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

Django ví, ke kterým polím se má připojit, protože to je deklarováno v Cuisine tabulka, která je vtažena pomocí through=Cuisine parametr v cuisine_types . také ví, že má provést dílčí dotaz, protože procházíte vztahem M2M.

Takže dostaneme SQL ekvivalentní k:

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

Napůl cesty tam. Nyní potřebujeme SELECT DISTINCT takže nezískáme více kopií stejného záznamu:

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

A pro zobrazení musíte vytáhnout typy kuchyní. Ukázalo se, že dotaz, který máte, je tam neefektivní, protože vás dostane pouze do tabulky spojení a musíte spustit další dotazy, abyste získali související CuisineType evidence. Hádej co:Django tě pokryl.

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

Django spustí dva dotazy:jeden, jako je ten váš, aby získal společné ID, a druhý pro získání souvisejícího CuisineType evidence. Potom se přístupy přes výsledek dotazu nemusí vracet do databáze.

Poslední dvě věci jsou uspořádání:

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

A LIMIT :

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

A tam je váš dotaz (a související dotaz) zabalený do dvou řádků Pythonu. Uvědomte si, že v tuto chvíli nebyl dotaz ani proveden. Než to něco udělá, musíte to do něčeho vložit, například do šablony:

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])
        })

Šablona:

{% 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. Jak mohu zapsat hodnoty SQLite Real do hodnot Java BigDecimal?

  2. Načtení počtu řádků s konkrétní hodnotou po filtrování dotazu pomocí nástroje pro výběr data

  3. Je možné v postgreSQL použít proměnnou a nespecifikovat návratový typ?

  4. Pro Nvarchar (Max) dostávám pouze 4000 znaků v TSQL?