2016-06-10 5 views
0


Я новичок в разработке django webapp, и у меня возникла проблема. Я создал 1 приложение с 1 моделью для вставки данных в базу данных с использованием 1 формы. Для этого я собираюсь использовать несколько баз данных. Каждая база данных будет иметь 1 таблицу (на данный момент) с той же структурой. Теперь у меня проблема:

Как я могу использовать только 1 модель, 1 вид и 1 форму для нескольких баз данных и их соответствующих таблиц. Базы данных и таблицы должны быть включены при вызове соответствующих URL-адресов.

, например. http://www.example.com/x/abc/ получит доступ к первой базе данных и ее таблицам для всех операций.
http://www.example.com/y/abc/ получит вторую базу данных

Я уже пробовал образец db-маршрутизации, предоставленный в документации django, но это мало помогает. также я не мог найти относительный пост/вопрос, который касается этой конкретной проблемы.
django одно приложение одна модель несколько баз данных

Я хочу сделать это, потому что позже я добавлю больше моделей и форм для доступа к данным из таблиц базы данных, и это кажется самым чистым способом мне

PS: Я использую Django 1.9.6

+1

и у вас должны быть отдельные базы данных, потому что ...? Вы рассматривали альтернативу использования сегмента пути URL как идентификатора и использования этого идентификатора в качестве внешнего ключа для фильтрации данных? – Brandon

+0

, потому что таблицы внутри них будут иметь одинаковую структуру, но разные данные, URL прямо сейчас не является большой проблемой. проблема заключается в «1 модели, 1 представлении и 1 форме для нескольких баз данных», чтобы избежать много дублирования. – rrawat

+1

Какая разница, если данные разные? Все это разделено внешним ключом. Подход внешнего ключа уменьшает вашу сложность на порядок. – Brandon

ответ

3

Независимо от того, является ли или не является хорошим способом для архитектора вашего приложения, вы можете сказать Django базы данных для чтения и записи с помощью :

Person.objects.using('db1').create(...) 
Person.objects.using('db2').create(...) 

Таким образом, вам не нужно использовать маршрутизатор, просто укажите две базы данных в своих настройках и запустите , перейдите на. Таблица вашей модели будет создана в каждой базе данных, а в вашем коде вы можете читать и записывать из двух баз данных на основе любой выбранной вами логики (например, на основе пути запроса).

См https://docs.djangoproject.com/en/1.9/topics/db/multi-db/#manually-selecting-a-database

+0

Вам также необходимо определить все базы данных в settings.py - на данный момент вы, вероятно, только определили базу данных по умолчанию – HenryM

+0

. Я также читал об этом подходе и буду использовать его. Но мне было интересно, есть ли другое решение. спасибо за помощь – rrawat

1

Я отвечаю из-за вашего «мне было интересно, действительно ли есть другое решение» комментарий. Там есть. У меня есть и работает на веб-сайте ... несколько баз данных SQLite с одним приложением. Вы также упомянули проблемы с маршрутизатором db, с которыми я тоже боролся.

Во-первых, разместить в любом месте в router.py файл, который содержит следующее:

class Router(object): 

    appname = '' 

    def db_for_read(self, model, **hints): 
     """ 
     Attempts to read self.appname models go to model.db. 
     """ 
     if model._meta.app_label == self.appname: 
      return model.db 
     return None 

    def db_for_write(self, model, **hints): 
     """ 
     Attempts to write self.appname models go to model.db. 
     """ 
     if model._meta.app_label == self.appname: 
      return model.db 
     return None 

    def allow_relation(self, obj1, obj2, **hints): 
     """ 
     Allow relations if a model in the self.appname app is involved. 
     """ 
     if obj1._meta.app_label == self.appname or \ 
      obj2._meta.app_label == self.appname: 
      return True 
     return None 

    # This is possibly the new way, for beyond 1.8. 
    ''' 
    def allow_migrate(self, db, app_label, model_name=None, **hints): 
     """ 
     Make sure the self.appname app only appears in the self.appname 
     database. 
     """ 
     if app_label == self.appname: 
      return db == self.appname 
     return None 
    ''' 

    # Contrary to Djano docs this one works with 1.8, not the one above. 
    def allow_migrate(self, db, model): 
      """ 
      Make sure the self.appname app only appears in the self.appname 
      database. 
      """ 
      if db == self.appname: 
       return model._meta.app_label == self.appname 
      elif model._meta.app_label == self.appname: 
       return False 
      return None 

Я испытал это только с Django 1.8; поскольку вы используете 1.9, вы, по крайней мере, должны будете использовать другие allow_migrate.

Обратите внимание, что: (1) в Router() нет базовых классов с атрибутами, что означает, что функция Python type может быть легко использована для клонирования; и, (2) очевидно, что текущий экземпляр модели доступен внутри Router() через внешнее пространство имен.

Так что теперь, в вашем models.py, сделайте следующее:

from django.db import models 

import os 
appname = os.path.dirname(__file__).split('/')[-1] 
from dirwhereyouputtherouter.router import Router 
router = type(appname, (Router,), dict(appname=appname)) 

class Book(models.Model): 

    # This is the default, for use when there is only one db per app. 
    db = appname 

    # the various fields here, etc. 

ли, что для каждой модели. И тогда Router() при попадании с любым запросом будет return model.db. Я выбираю здесь, по моей схеме, сохранить стандартную схему Django ... приложения, получая его имя от имени его каталога.

Теперь, в settings.py, вам необходимо DATABASE_ROUTERS = [ 'appdir.models.router', ]. Это направляет запрос через router, который мы инициализировали в models.py, используя функцию type(). Обратите внимание, что я не перечисляю базу данных default здесь, в DATABASE_ROUTERS. У меня есть один, который указан в настройке DATABASES с ключом default. Когда вы выполняете свои первоначальные миграции, различные таблицы приложений Django попадают в базу данных default, по умолчанию, конечно, так как Router() отступит.

Итак, в вашем представлении вы начнете с Book.db = x, где будет отображаться подпись вида myview(request, x, someothername):. Есть вероятность, что вы захотите задрапировать весь корпус тела с помощью try: finally:, где в блоке finally вы вернете выбор базы данных по умолчанию.

Должен признаться, что может произойти муха в мазе, когда дело доходит до миграции. Я ненавижу их, и в моем случае я пишу все содержимое моих маленьких баз данных SQLite, когда я их обновляю, что часто. Поэтому, не заботясь о сохранении данных, я бросаю их и папки Migrations, когда мне нужно вносить изменения. Но если ваши обстоятельства разные, вам, возможно, придется бороться с командами migrate и makemigrations. Каждая из моих баз данных имеет одинаковую схему, определенную моделью. Поэтому я фактически создал пустую базу данных, сначала временно помещая запись в настройку DATABASES, которая имела в качестве ключа имя приложения. Я использовал это, чтобы создать одну копию файла SQLite db, а затем просто скопировал его и переименовал, когда захотел добавить новую базу данных (добавив данные для новой базы данных в DATABASES и удалив временную запись с именем приложения в качестве ключа). Кроме того, вы можете сохранить и использовать базу данных, ключ которой находится в DATABASES - это имя приложения.

Но, к сожалению, мы не закончили. Я должен был исправить admin.py. После создания в admin.pyclass BookAdmin(admin.ModelAdmin): обычным способом вы не найдете свои две базы данных, доступные в admin. Так что мой admin.py выглядит следующим образом:

from django.contrib import admin 
from django.conf import settings 
import os 

class BookAdmin(admin.ModelAdmin): 

    list_display = ... 
    list_filter = ... 
    etc. 

from models import Book 


appname = os.path.dirname(__file__).split('/')[-1] 

dbkeys = settings.DATABASES.keys() 
while 'default' in dbkeys: dbkeys.remove('default') 
dbkeys = [ k for k in dbkeys if os.path.dirname(settings.DATABASES[k]['NAME']).split(os.sep)[-1] == appname ] 

for dbname in dbkeys: 

    if dbname == dbkeys[0]: 

     class Book_(Book): 
      class Meta: 
       proxy = True 
       verbose_name = Book.__name__ + '_' + ''.join('_' + x.capitalize() or '_' for x in dbname.split('_')) 
       verbose_name_plural = Book.__name__ + 's_'+ ''.join('_' + x.capitalize() or '_' for x in dbname.split('_')) 
       db_table = appname + '_book' 
     Book_.db = dbname 
     Book_.__name__ = Book.__name__ + '_' + appname.capitalize() + '_' + ''.join('_' + x.capitalize() or '_' for x in dbname.split('_')) 
     admin.site.register(Book_, BookAdmin) 


    elif dbname == dbkeys[1]: 

     class Book__(Book): 
      class Meta: 
       proxy = True 
       verbose_name = Book.__name__ + '_' + ''.join('_' + x.capitalize() or '_' for x in dbname.split('_')) 
       verbose_name_plural = Book.__name__ + 's_'+ ''.join('_' + x.capitalize() or '_' for x in dbname.split('_')) 
       db_table = appname + '_book' 
     Book__.db = dbname 
     Book__.__name__ = Book.__name__ + '_' + appname.capitalize() + '_' + ''.join('_' + x.capitalize() or '_' for x in dbname.split('_')) 
     admin.site.register(Book__, BookAdmin) 

Это работает, потому что я поставил файлы БД для каждого приложения в папке этого приложения. Извините, все это немного сложно. У меня были веские причины для желаний. См. Также мой оставшийся без ответа вопрос о моделях подкласса для их регистрации с использованием admin.site.register, here.

Смежные вопросы