Kerangka kerja "sites"

Django datang dengan sebuah pilihan kerangka kerja "sites". Itu adalah pengait untuk obyek-obyek terkait dan kegunaannya untuk situs jaringan tertentu, dan itu adalah tempat menahan untuk nama ranah dan nama-nama "verbose" dari situs-situs anda ditenagai-Django.

Gunakan itu jika pemasangan Django tunggal anda mentenagai lebih dari satu situs dan anda butuh membedakan diantara situs-situs tersebut dalam beberapa cara.

Kerangka kerja situs utamanya berdasarkan pada model sederhana:

class models.Site

Sebuah model untuk menyimpan atribut domain dan name dari situs jaringan.

domain

Nama ranah memenuhi syarat sepenuhnya terkait dengan situs jaringan. Sebagai contoh, www.example.com.

name

Nama "verbose" dibaca-manusia untuk situ sjaringan.

Pengaturan SITE_ID menentukan ID basisdata dari obyek Site terkait dengan berkas pengaturan tertentu itu. Jika pengaturan dihilangkan, fungsi get_current_site() akan mencoba untuk mendapatkan situs saat ini dengan membandingkan domain dengan nama rumah dari metode request.get_host().

Bagaimana anda menggunakannya terserah anda, tetapi Django menggunakan itu dalam sepasang cara otomatis melalui kebiasanaan sederhana.

Contoh penggunaan

Mengapa anda menggunakan situs? Ini adalah penjelasan terbaik melalui contoh.

Isi terkait dengan situs-situs banyak

Situs-situs ditenagai-Django LJWorld.com dan Lawrence.com dikendalikan oleh organisasi berita yang sama -- surat kabar Lawrence Journal-World di Lawrence, Kansas. LJWorld.com fokus pada berita, selagi Lawrence.com fokus pada hiburan lokal. Tetapi terkadang penyunting ingin menerbitkan sebuah artikel pada kedua situs.

Cara pemecahan tidak dibuat-buat dari memecahkan masalah yaitu membutuhkan pembuat situs untuk menerbitkan cerita sama dua kali: sekali untuk LJWorld.com dan kembali untuk Lawrence.com. Tetapi itu tidak efesien untuk pembuat situs, dan itu berulang untuk menyimpan banyak salinan dari cerita sama dalam basisdata.

Pemecahan terbaik adalah sederhana: Kedua situs menggunakanbasisdata artikel yang sama, dan sebuah artikel berhubungan dengan satu atau lebih situs. Dalam istilah model Django, yaitu diwakili oleh sebuah ManyToManyField dalam model Article:

from django.contrib.sites.models import Site
from django.db import models

class Article(models.Model):
    headline = models.CharField(max_length=200)
    # ...
    sites = models.ManyToManyField(Site)

Ini menyelesaikan beberapa hal cukup baik:

  • Itu membiarkan pembuat situs menyunting semua isi -- pada kedua situs -- dalam antarmuka tunggal (admin Django).

  • Itu berarti cerita sams tidak harus menerbitkan kedua kali di basisdata; itu hanya mempunyai rekaman tunggal di basisdata.

  • Itu membiarkan pengembang situs menggunakan kode tampilan Django sama untuk kedua situs. Kode tampilan yang memperlihatkan cerita yang diberikan cukup memeriksa untuk memastikan cerita diminta pada situs saat ini. Itu terlihat sesuatu seperti ini:

    from django.contrib.sites.shortcuts import get_current_site
    
    def article_detail(request, article_id):
        try:
            a = Article.objects.get(id=article_id, sites__id=get_current_site(request).id)
        except Article.DoesNotExist:
            raise Http404("Article does not exist on this site")
        # ...
    

Isi terkait dengan situs tunggal

Demikian pula, anda dapat menghubungkan sebuah model ke model Site dalam sebuah hubungan many-to-one, menggunakan ForeignKey.

Sebagai contoh, jika sebuah artikel hanya diizinkan pada situs tunggal, anda akan menggunakan model seperti ini:

from django.contrib.sites.models import Site
from django.db import models

class Article(models.Model):
    headline = models.CharField(max_length=200)
    # ...
    site = models.ForeignKey(Site, on_delete=models.CASCADE)

Ini mempunyai keuntungan sama seperti digambarkan di bagian terakhir.

Mengaitkan kedalam situs saat ini dari tampilan

Anda dapat menggunakan kerangka kerja situs di tampilan Django anda untuk melakukan hal-hal tertentu berdasarkan pada situs dimana tampilan sedang dipanggil. Sebagai contoh:

from django.conf import settings

def my_view(request):
    if settings.SITE_ID == 3:
        # Do something.
        pass
    else:
        # Do something else.
        pass

Tentu saja, itu adalah kode-keras buruk ID situs seperti itu. pengkodean-keras semacam ini adalah terbaik untuk diperbaiki yang anda butuh diselesaikan cepat. Cara terbersih dari memenuhi hal sama adalah memeriksa ranah situs saat ini:

from django.contrib.sites.shortcuts import get_current_site

def my_view(request):
    current_site = get_current_site(request)
    if current_site.domain == 'foo.com':
        # Do something
        pass
    else:
        # Do something else.
        pass

Ini juga mempunyai keuntungan dari pemeriksaan jika kerangka kerja situs dipasang, dan mengembalikan contoh RequestSite jika itu tidak.

Jika anda tidak mempunyai akses ke obyek diminta, anda dapat menggunakan metode get_current() dari pengelola model Site. Anda harus kemudian memastikan bahwa berkas pengaturan anda mengandung pengaturan SITE_ID. Contoh ini adalah setara pada satu sebelumnya:

from django.contrib.sites.models import Site

def my_function_without_request():
    current_site = Site.objects.get_current()
    if current_site.domain == 'foo.com':
        # Do something
        pass
    else:
        # Do something else.
        pass

Mendapatkan ranah saat ini untuk ditampilkan

LJWorld.com dan Lawrence.com keduanya memiliki kegunakan peringatan surel, yang membuat pembaca masuk untuk mendapatkan pemberitahuan ketika berita terjadi. itu adalah dasar: Seorang pembaca masuk pada formulir Jaringan dan segera mendapatkan sebuah surel berkata, "Terima kasih untuk langganan anda."

Itu sangat tidak efesien dan berulang untuk menerapkan kode pengolahan daftar ini dua kali, jadi situs menggunakan kode sama dibelakang layar. Tetapi pemberitahuan "thank you for signing up" butuh berbeda untuk setiap situs. Dengan menggunakan obyek Site, kami dapat meringkaskan pemberitahuan "thank you" untuk menggunakan nilai dari name dan domain situs saat ini.

Disini adalah contoh dari apa tampilan penanganan formulir kelihatannya:

from django.contrib.sites.shortcuts import get_current_site
from django.core.mail import send_mail

def register_for_newsletter(request):
    # Check form values, etc., and subscribe the user.
    # ...

    current_site = get_current_site(request)
    send_mail(
        'Thanks for subscribing to %s alerts' % current_site.name,
        'Thanks for your subscription. We appreciate it.\n\n-The %s team.' % (
            current_site.name,
        ),
        'editor@%s' % current_site.domain,
        [user.email],
    )

    # ...

Pada Lawrence.com, surel ini mempunyai baris judul "peringatan Terima kasih untuk berlanggakan di lawrence.co." Pada LJWorld.com, surel mempunyai judul "peringatan Terima kasih untuk berlangganan di LJWorld.com." Sama seperti badan pesan surel.

Catat bahwa bahkan lebih elastis (tetapi lebih kelas berat) cara melakukan ini akan menggunakan sistem cetakan Django. Menganggap Lawrence.com dan LJWorld.com mempunyai direktori cetakan berbeda (DIRS), anda cukup bertani sistem cetakan seperti itu:

from django.core.mail import send_mail
from django.template import Context, loader

def register_for_newsletter(request):
    # Check form values, etc., and subscribe the user.
    # ...

    subject = loader.get_template('alerts/subject.txt').render(Context({}))
    message = loader.get_template('alerts/message.txt').render(Context({}))
    send_mail(subject, message, '[email protected]', [user.email])

    # ...

Dalam kasus ini, anda harus membuat berkas cetakan subject.txt dan message.txt untuk kedua direktori cetakan LJWorld.com and Lawrence.com. Itu memberikan anda lebih keluwesan, tetapi itu juga lebih rumit.

itu adalah ide bagus membuka obyek Site sebanyak mungkin untuk memindahkan kerumitan dan perulangan yang tidak diperlukan.

Mendapatkan ranah saat ini untuk URL penuh

Kaidah get_absolute_url() Django baik untuk mendapatkan URL obyek anda tanpa nama ranah, tetapi dalam beberapa kasus anda mungkin ingin menampilkan URL penuh -- dengan http:// dan ranah dan semuanya -- untuk sebuah obyek. Untuk melakukan ini, anda dapat menggunakan kerangka kerja situs. Contoh sederhana:

>>> from django.contrib.sites.models import Site
>>> obj = MyModel.objects.get(id=3)
>>> obj.get_absolute_url()
'/mymodel/objects/3/'
>>> Site.objects.get_current().domain
'example.com'
>>> 'https://%s%s' % (Site.objects.get_current().domain, obj.get_absolute_url())
'https://example.com/mymodel/objects/3/'

Mengadakan kerangka situs

Untuk mengadakan kerangka situs ini, ikuti langkah-langkah ini:

  1. Menambahkan 'django.contrib.sites' ke pengaturan INSTALLED_APPS anda.

  2. Menentukan pengaturan SITE_ID:

    SITE_ID = 1
    
  3. Menjalankan migrate.

django.contrib.sites mendaftarkan penangan sinyal post_migrate yang membuat situs awalan bernama example.com dengan ranah example.com. Situs ini akan juga dibuat setelah Django basisdata percobaan. Untuk mensetel nama benar dan ranah untuk proyek anda, anda dapat menggunakan data migration.

Untuk melayani situs berbeda dalam produksi, anda aakn membuat berkas pengaturan dengan setiap SITE_ID (mungkin mengimpor dari berkas pengaturan umum untuk menghindari penggandaan pengaturan berbagi) dan kemudian menentukan DJANGO_SETTINGS_MODULE sesuai untuk setiap situs.

Men cache obyek Site saat ini

Ketika situs saat ini disimpan dalam basisdata, setiap panggilan pada Site.objects.get_current() dapat menghasilkan sebuah permintaan basisdata. Tetapi Django sedikit lebih pintar pada permintaan pertama, situs saat ini disimpan sementara, dan panggilan selanjutnya apapun mengembalikan data disimpan sementara daripada mengenai basisdata.

Jika untuk alasan apapun anda ingin memaksa permintaan basisdata, anda dapat memberitahu Django untuk membersihkan cache menggunakan Site.objects.clear_cache():

# First call; current site fetched from database.
current_site = Site.objects.get_current()
# ...

# Second call; current site fetched from cache.
current_site = Site.objects.get_current()
# ...

# Force a database query for the third call.
Site.objects.clear_cache()
current_site = Site.objects.get_current()

CurrentSiteManager

class managers.CurrentSiteManager

Jika Site memainkan sebuah peran kunci dalam apliaksi anda, pertimbangkan menggunakan CurrentSiteManager membantu dalam model anda. Itu adalah sebuah model manager yang otomatis menyaring permintaannya untuk menyertakan obyek apapun berkaitan dengan Site saat ini.

SITE_ID wajib

CurrentSiteManager hanya berguna ketika pengaturan SITE_ID ditentukan dalam pengaturan anda.

Gunakan CurrentSiteManager dengan menambahkan itu ke model anda dengan tegas. Sebagai contoh:

from django.contrib.sites.models import Site
from django.contrib.sites.managers import CurrentSiteManager
from django.db import models

class Photo(models.Model):
    photo = models.FileField(upload_to='photos')
    photographer_name = models.CharField(max_length=100)
    pub_date = models.DateField()
    site = models.ForeignKey(Site, on_delete=models.CASCADE)
    objects = models.Manager()
    on_site = CurrentSiteManager()

Dengan model ini, Photo.objects.all() akan mengembalikan semua obyek Photo dalam basisdata, tetapi Photo.on_site.all() akan mengembalikan hanya obyek Photo berkaitan dengan situs saat ini, menurut pada pengaturan SITE_ID.

Taruh di jalan lain, dua pernyataan ini adalah sama:

Photo.objects.filter(site=settings.SITE_ID)
Photo.on_site.all()

Bagaimana CurrentSiteManager mengetahui bidang mana dari Photo adalah the Site? Secara awalan, CurrentSiteManager mencari salah satu ForeignKey dipanggil site atau ManyToManyField dipanggil sites untuk menyaring. Jika anda menggunakan sebuah bidang bernama sesuatu selain dari site atau sites` untuk mencirikan obyek Site mana terkait, kemudian butuh secara tegas melewatkan nama bidang penyesuaian sebagai parameter pada CurrentSiteManager di model anda. Model berikut, yang mempunyai bidang disebut publish_on, mempertunjukkan ini:

from django.contrib.sites.models import Site
from django.contrib.sites.managers import CurrentSiteManager
from django.db import models

class Photo(models.Model):
    photo = models.FileField(upload_to='photos')
    photographer_name = models.CharField(max_length=100)
    pub_date = models.DateField()
    publish_on = models.ForeignKey(Site, on_delete=models.CASCADE)
    objects = models.Manager()
    on_site = CurrentSiteManager('publish_on')

Jika anda berusaha menggunakan CurrentSiteManager dan melewatkan sebuah bidang nama yang tidak ada, Django akan memunculkan sebuah ValueError.

Akhirnya, catat bahwa anda mungkin ingin menjaga Manager biasa (bukan-situs-khusus) pada model anda, bahkan jika anda menggunakan CurrentSiteManager. Seperti dijelaskan dalam manager documentation, jika anda menentukan pengelola secara manual, kemudian Django tidak akan membuat pengeloa objects = models.Manager() otomatis untuk anda. Juga catat bahwa bagian tertentu dari Django -- yaitu, situs admin Django dan tampilan umum -- gunakan pengelola mana saja ditentukan pertama dalam model, jadi jika anda ingin situs admin memiliki akses ke semua obyek (bukan hanya satu situs-khusus), taruh objects = models.Manager() dalam model anda, sebelum anda menentukan CurrentSiteManager.

Situs middleware

Jika anda sering menggunakan pola ini:

from django.contrib.sites.models import Site

def my_view(request):
    site = Site.objects.get_current()
    ...

ada cara sederhana untuk menghindari perulangan. tambah django.contrib.sites.middleware.CurrentSiteMiddleware pada MIDDLEWARE. Middleware menyetel atribut site pada setiap permintaan obyek, jadi anda dapat menggunakan request.site untuk mendapatkan situs saat ini.

Bagaimana Django menggunakan kerangka situs

Meskipun itu tidak diwajibkan bahwa anda menggunakan kerangka kerja situs, itu sangat dianjurkan, karena Django mengambil keuntungan dari itu dalam sedikit tempat. bahkan jika pemasangan Django anda menenagai hanya situs tunggal, anda harus mengambil dua detik untuk membuat obyek situs dengan domain dan name, dan menunjuk ke ID dalam pengaturan SITE_ID anda.

Ini adalah bagaimana Django menggunakan kerangka situs:

  • Dalam redirects framework, setiap obyek pengalihan berkaitan dengan situs stertentu. Ketika Django mencari untuk pengalihan, itu menghitung situs saat ini.
  • Dalam flatpages framework, setiap flatpage dihubungkan dengan situs tertentu. Ketika sebuah flatpage dibuat, anda menentukan Site nya, dan FlatpageFallbackMiddleware memeriksa situs saat ini dalam mengambil flatpage untuk dimunculkan.
  • Dalam syndication framework, cetakan untuk title dan description otomatis mempunyai akses ke variabel {{site}}, yaitu obyek which is the Site mewakili situs saat ini. Juga, kaitan untuk menyediakan barang URL akan menggunakan domain dari obyek Site saat ini jika anda tidak menentukan ranah berkualitas-sepenuhnya.
  • Dalam authentication framework, django.contrib.auth.views.LoginView melewatkan nama Site sat ini ke cetakan sebagai {{ site_name }}.
  • Tampilan jalan pintas (django.contrib.contenttypes.views.shortcut) menggunakan ranah dari obyek Site saat ini ketika menghitung sebuah URL obyek.
  • Dalam kerangka kerja admin, tautan "lihat pada situs" menggunakan Site saat ini untuk bekerja ranah untuk situs yang itu akan alihkan.

Obyek RequestSite

Beberapa aplikasi django.contrib mengambil keuntungan dari kerangka kerja situs tetapi dirancang dengan cara yang tidak membutuhkan kerangka kerja situs untuk dipasang dalam basisdata anda. (Beberapa orang tidak ingin, atau hanya tidak dapat memasang tabel tambahan basisdata yang kerangka kera situs butuhkan.) Untuk kasus-kasus itu, kerangka kerja menyediakan sebuah kelas django.contrib.sites.requests.RequestSite,yang dapat digunakan sebagai sebuah fallback ketika dukungan-basisdata kerangka kerja situs tidak tersedia.

class requests.RequestSite

Sebuah kelas yang berbagi antarmuka utama dari Site (yaitu, itu memiliki atribut domain dan name) tetapi mendapatkan datanya dari obyek HttpRequest Django daripada dari sebuah basisdata.

__init__(request)

Setel atribut name dan domain ke nilai dari get_host().

Sebuah obyek RequestSite mempunyai antarmuka mirip pada obyek Site biasa, kecuali metode __init__() nya mengambil sebuah obyek HttpRequest. Itu dapat menyimpulkan domain dan name dengan mencari domain permintaan. Itu mempunyai metode save() dan delete() untuk mencocokkan antarmuka dari Site, tetapi metode-metode memunculkan NotImplementedError.

Jalan pintas get_current_site

Akhirnya, untuk menghindari kode fallback berulang, kerangka kerja menyediakan sebuah fungsi django.contrib.sites.shortcuts.get_current_site()

shortcuts.get_current_site(request)

Sebuah fungsi yang memeriksa jika django.contrib.sites dipasang dan mengembalikan salah satu obyek Site object saat ini atau obyek RequestSite berdasarkan pada permintaan. Itu mencari situs saat ini berdasarkan pada request.get_host() jika pengaturan SITE_ID tidak ditentukan.

Kedua ranah dan port mungkin dikembalikan oleh request.get_host() ketika kepala Host mempunyai port dengan jelas ditentukan, misalnya example.com:80. Dalam kasus tersebut, jika pencarian gagal karena host tidak cocok dengan sebuah rekaman dalam basisdata, port dihilangkan dan pencarian dicoba kembali dengan hanya bagian ranah. Ini tidak berlaku pada RequestSite yang akan selalu menggunakan hos tidak berubah.

Back to Top