sql >> Database teknologi >  >> RDS >> Mysql

Django views.py Version af SQL Join med Multi Table Query

Nå, det er nogle uklare tabel- og feltnavne, men bedst jeg kan se, at forespørgslen ville se sådan ud:

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

Men medmindre du er låst ind i det databaseskema, ville dine modeller se bedre ud som:

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)

Så ville forespørgslen mere ligne:

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

OK, lad os gennemgå din forespørgsel, forudsat at der ikke er ændringer i din kode. Vi starter med underforespørgslen.

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

Vi kigger på WHERE-klausulen og ser, at vi har brug for en JOIN. For at lave en joinforbindelse skal du erklære et relationelt felt i en af ​​de sammenføjede modeller (Django tilføjer en omvendt relation, som vi bør navngive). Så vi matcher cuisine.cuisineid med `cuisinetype.cuisineid. Det er noget forfærdeligt navn.

Det er en mange-til-mange-relation, så vi har brug for et ManyToManyField . Nå, ser på Cuisine model, er det virkelig sammenføjningsbordet til denne M2M. Django forventer, at en sammenføjningstabel har to ForeignKey felter, der peger på hver side af leddet. Normalt vil det skabe dette for dig for at redde fornuften. Du er åbenbart ikke så heldig. Så du skal manuelt tilslutte den.

Det ser ud til, at "GID"-feltet er et (ubrugeligt) ID-felt for posten, så lad os antage, at det er et heltal med automatisk stigning. (For at være sikker, tjek CREATE TABLE-kommandoerne.) Nu kan vi omskrive Cuisine model til noget, der nærmer sig fornuftigt:

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'

Modelnavnene er citeret, fordi modellerne ikke er defineret endnu (de er senere i filen). Nu er der intet krav om, at Django-feltnavnene matcher kolonnenavnene, så lad os ændre dem til noget mere læsbart. Post-id-feltet hedder normalt bare id , og fremmednøgler er normalt opkaldt efter det, de relaterer til:

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, vi er færdige med at definere vores fælles bord. Mens vi er ved dette, lad os anvende de samme ting på vores Cuisinetype model. Bemærk det rettede kamelhus-klassenavn:

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'

Så vi kommer endelig til vores Restaurant model. Bemærk, at navnet er ental; et objekt repræsenterer kun én post.

Jeg bemærker, at den mangler nogen dp_table eller db_column ting, så jeg går ud og gætter på, at Django skaber det. Det betyder, at vi kan lade det skabe id felt for os, og vi kan udelade det fra vores kode. (Hvis det ikke er tilfældet, så tilføjer vi det bare ligesom med de andre modeller. Men du burde virkelig ikke have et nullbart registrerings-id.) Og det er her vores køkkentype ManyToManyField liv:

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)

Bemærk, at navnet på M2M-feltet er flertal, da denne relation fører til flere poster.

En ting mere, vi ønsker at tilføje til denne model, er navne for de omvendte forhold. Med andre ord, hvordan man går fra de andre modeller tilbage til Restaurant . Det gør vi ved at tilføje related_name parametre. Det er ikke usædvanligt, at de er ens.

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

Nu er vi endelig klar. Så lad os se på din forespørgsel:

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

Da dette er FROM restaurants , starter vi med modellens standardobjektmanager, objects :

Restaurant.objects

WHERE klausul i dette tilfælde er en filter() opkald, så vi tilføjer det for første termin:

Restaurant.objects.filter(city=8)

Du kan have en primær nøgleværdi eller en City objekt på højre side af dette udtryk. Resten af ​​forespørgslen bliver dog mere kompleks, fordi den har brug for JOIN . En joinforbindelse i Django ligner bare en dereferencing gennem relationsfeltet. I en forespørgsel betyder det at sammenføje de relevante feltnavne med en dobbelt understregning:

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

Django ved, hvilke felter der skal deltage i, fordi det er deklareret i Cuisine tabel, som trækkes ind af through=Cuisine parameter i cuisine_types . den ved også at lave en underforespørgsel, fordi du gennemgår en M2M-relation.

Så det giver os SQL svarende til:

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

Halvvejs der. Nu skal vi bruge SELECT DISTINCT så vi ikke får flere kopier af den samme post:

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

Og du skal trække i køkkentyperne til fremvisning. Det viser sig, at den forespørgsel, du har, er ineffektiv der, fordi den kun fører dig til join-tabellen, og du skal køre yderligere forespørgsler for at få den relaterede CuisineType optegnelser. Gæt hvad:Django har dig dækket.

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

Django vil køre to forespørgsler:en som din for at få de fælles ID'er, og en mere for at få den relaterede CuisineType optegnelser. Så behøver adgang via forespørgselsresultatet ikke gå tilbage til databasen.

De sidste to ting er rækkefølgen:

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

Og LIMIT :

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

Og der er din forespørgsel (og den relaterede forespørgsel) pakket ind i to linjer af Python. Husk på, på dette tidspunkt er forespørgslen ikke engang blevet udført. Du skal sætte det i noget, som en skabelon, før det gør noget:

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

Skabelon:

{% 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. få alle varer i kategorien og dens underordnede

  2. MySQL DROP INDEX

  3. Sådan installeres Adminer på sin egen app

  4. Sådan indstilles max_connections i MySQL Programmatisk