Mysql
 sql >> Teknologi Basis Data >  >> RDS >> Mysql

Django views.py Versi SQL Gabung dengan Multi Tabel Query

Nah, itu adalah beberapa nama tabel dan bidang yang tidak jelas, tetapi saya dapat mengatakan bahwa kueri akan terlihat seperti:

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

Tetapi kecuali Anda terkunci dalam skema database itu, model Anda akan terlihat lebih baik sebagai:

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)

Maka kuerinya akan lebih seperti:

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

Oke, mari kita telusuri kueri Anda, dengan asumsi tidak ada perubahan pada kode Anda. Kita akan mulai dengan subquery.

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

Kami melihat klausa WHERE dan melihat kami membutuhkan JOIN. Untuk melakukan penggabungan, Anda harus mendeklarasikan bidang relasional dalam salah satu model yang digabungkan (Django akan menambahkan hubungan terbalik, yang harus kita beri nama). Jadi kami mencocokkan cuisine.cuisineid dengan `cuisinetype.cuisineid. Itu beberapa penamaan yang mengerikan.

Itu adalah relasi banyak ke banyak, jadi kita membutuhkan ManyToManyField . Nah, melihat Cuisine model, itu benar-benar tabel bergabung untuk M2M ini. Django mengharapkan tabel penggabungan memiliki dua ForeignKey bidang, satu menunjuk ke setiap sisi sambungan. Biasanya itu akan membuat Anda untuk menyimpan kewarasan. Rupanya Anda tidak seberuntung itu. Jadi, Anda harus menghubungkannya secara manual.

Tampaknya bidang "GID" adalah bidang ID (tidak berguna) untuk dicatat, jadi mari kita asumsikan itu bilangan bulat kenaikan otomatis. (Untuk memastikannya, periksa perintah CREATE TABLE.) Sekarang kita dapat menulis ulang Cuisine model menjadi sesuatu yang mendekati waras:

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'

Nama model dikutip karena modelnya belum ditentukan (mereka nanti ada di file). Sekarang tidak ada persyaratan bahwa nama bidang Django cocok dengan nama kolom, jadi mari kita mengubahnya menjadi sesuatu yang lebih mudah dibaca. Kolom ID catatan biasanya hanya bernama id , dan kunci asing biasanya dinamai menurut apa yang terkait dengannya:

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, kita sudah selesai mendefinisikan tabel bersama kita. Saat ini, mari kita terapkan hal yang sama ke Cuisinetype our model. Perhatikan nama kelas kotak unta yang dikoreksi:

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'

Akhirnya kami sampai di Restaurant model. Perhatikan bahwa namanya tunggal; sebuah objek hanya mewakili satu record.

Saya perhatikan bahwa itu tidak memiliki dp_table atau db_column hal-hal, jadi saya mengambil risiko dan menebak Django yang menciptakannya. Itu berarti kita bisa membiarkannya membuat id bidang untuk kami dan kami dapat menghilangkannya dari kode kami. (Jika bukan itu masalahnya, maka kami hanya menambahkannya seperti dengan model lain. Tapi Anda benar-benar tidak boleh memiliki ID catatan yang dapat dibatalkan.) Dan di sinilah masakan kami ketik ManyToManyField hidup:

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)

Perhatikan bahwa nama bidang M2M adalah jamak, karena relasi tersebut mengarah ke beberapa record.

Satu hal lagi yang ingin kita tambahkan ke model ini adalah nama untuk hubungan terbalik. Dengan kata lain, bagaimana beralih dari model lain kembali ke Restaurant . Kami melakukan ini dengan menambahkan related_name parameter. Bukan hal yang aneh bagi mereka untuk menjadi sama.

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

Sekarang kita akhirnya siap. Jadi mari kita lihat kueri Anda:

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

Karena ini FROM restaurants , kita akan mulai dengan manajer objek default model itu, objects :

Restaurant.objects

WHERE klausa dalam hal ini adalah filter() panggilan, jadi kami menambahkannya untuk istilah pertama:

Restaurant.objects.filter(city=8)

Anda dapat memiliki nilai kunci utama atau City objek di sisi kanan istilah itu. Namun, sisa kueri menjadi lebih kompleks, karena memerlukan JOIN . Gabung di Django hanya terlihat seperti dereferensi melalui bidang relasi. Dalam kueri, itu berarti menggabungkan nama bidang yang relevan dengan garis bawah ganda:

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

Django mengetahui bidang mana yang akan digabungkan karena itu dideklarasikan dalam Cuisine tabel yang ditarik oleh through=Cuisine parameter di cuisine_types . itu juga tahu untuk melakukan subquery karena Anda sedang melalui hubungan M2M.

Sehingga membuat kita setara dengan SQL:

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

Setengah jalan di sana. Sekarang kita membutuhkan SELECT DISTINCT jadi kami tidak mendapatkan banyak salinan dari catatan yang sama:

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

Dan Anda perlu menarik jenis masakan untuk dipajang. Ternyata kueri yang Anda miliki tidak efisien di sana, karena hanya membawa Anda ke tabel gabungan dan Anda perlu menjalankan kueri lebih lanjut untuk mendapatkan CuisineType terkait catatan. Coba tebak:Django telah Anda liput.

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

Django akan menjalankan dua kueri:satu seperti milik Anda untuk mendapatkan ID gabungan, dan satu lagi untuk mendapatkan CuisineType terkait catatan. Kemudian akses melalui hasil query tidak perlu kembali ke database.

Dua hal terakhir adalah urutannya:

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

Dan LIMIT :

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

Dan ada kueri Anda (dan kueri terkait) yang dikemas ke dalam dua baris Python. Pikiran Anda, pada titik ini, kueri bahkan belum dieksekusi. Anda harus memasukkannya ke dalam sesuatu, seperti template, sebelum melakukan apa pun:

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

Templat:

{% 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. Apa yang setara dengan sqlite dari MySQL's INTERVAL dan UTC_TIMESTAMP?

  2. Skema XSD ke MySQL

  3. Menemukan posisi angka dalam string

  4. MySQL One-to-Many ke format JSON

  5. Apakah ada JDBC mysql yang akan menghormati fetchSize?