Django как проверить форму в CreateView при использовании foreignkeys и нескольких баз данных

Здравствуйте Это мой первый вопрос, поэтому, пожалуйста, простите за форматирование:

Текущая лабораторная установка

1 проект:

библиотека

1 приложение

каталог

2 базы данных

library_admin_db (admin-mysql)

catalog_db (mysql1

)

Я пытаюсь добавить объект базы данных в базу данных "catalog_db", используя представление на основе класса "CreateView"

Я уже установил соединение с базами данных:

DATABASES = {
# Must use Default
'default': {
    'ENGINE': 'django.db.backends.mysql',
    'NAME': 'library_admin_db',
    'USER': 'root',
    'PASSWORD': 'password',
    'HOST': '192.168.164.128',
    'PORT': '8002'
},
'catalog': {
    'ENGINE': 'django.db.backends.mysql',
    'NAME': 'catalog_db',
    'USER': 'root',
    'PASSWORD': 'password',
    'HOST': '192.168.164.128',
    'PORT': '8000',
}

}

Я установил DATABASE_ROUTERS:

DATABASE_ROUTERS = [
    BASE_DIR / 'routers.db_routers.LibraryRouter'# the "MyApp1Router is the class inside the db_routers file"
]

вот моя модель с внешними ключами:

from django.db import models
from django.urls import reverse
import uuid

# Create your models here.
class Genre(models.Model):
    name = models.CharField(max_length=150)

    def __str__(self):
        return self.name

class Book(models.Model):
    
    title = models.CharField(max_length=200)
    author = models.ForeignKey('Author', on_delete=models.SET_NULL, null=True)
    summary = models.TextField(max_length=600)
    isbn = models.CharField('ISBN', max_length=13, unique=True)
    genre = models.ManyToManyField(Genre)
    language = models.ForeignKey('language', on_delete=models.SET_NULL, null=True)


    def __str__(self):
        return self.title
    
    def get_absolute_url(self):
        return reverse('book_detail', kwargs={"pk":self.pk})

class Language(models.Model):
    name = models.CharField(max_length=200)
    def __str__(self):
        return self.name

class Author(models.Model):
    
    first_name = models.CharField(max_length=200)
    last_name = models.CharField(max_length=200)
    date_of_birth = models.DateField(null=True,blank=True)

    class Meta:
        ordering = ['last_name','first_name']
    
    def get_absolute_url(self):
        return reverse('author_detail', kwargs={"pk":self.pk})
    
    def __str__(self):
        return f"{self.last_name} {self.first_name}"

class BookInstance(models.Model):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4)
    book = models.ForeignKey('Book', on_delete=models.RESTRICT, null=True)
    imprint = models.CharField(max_length=200)
    due_back = models.DateField(null=True, blank=True)

    LOAN_STATUS = (
        ('m', "Maintenance"),
        ('o', 'On Loan'),
        ('a', 'Available'),
        ('r', 'Reserved')
    )
    status = models.CharField(max_length=1, choices=LOAN_STATUS, blank=True, default='m')

    class Meta:
        ordering = ['due_back']

    def __str__(self):
        return f'{self.id} ({self.book.title})'

Вот вид:

from django.shortcuts import render
from catalog.models import Book, BookInstance, Author, Genre, Language
from django.views.generic import CreateView
from django.urls import reverse_lazy


# Create your views here.

class BookCreateView(CreateView):
    model = Book
    fields = ['title', 'author', 'summary', 'isbn', 'genre', 'language']
    # queryset = Book.objects.using('catalog') # Must use this if using a secondary database! if not using secondary database then this is automated!
    success_url = reverse_lazy('catalog:home')

    

    def form_valid(self, form):
        temp = form.save(commit=False)
        temp.save(using='catalog')
        print('Hello')
        return super().form_valid(form)

    def form_invalid(self, form):
        print('form_invalid') 
        return super().form_invalid(form)

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['form'].fields['author'].queryset = Author.objects.using('catalog')
        context['form'].fields['language'].queryset = Language.objects.using('catalog').all()
        context['form'].fields['genre'].queryset = Genre.objects.using('catalog').all()
        return context

Вот шаблон Jinja с формой:

<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <h1>Teacher Form</h1>
    <form method="POST">
        {% csrf_token %}
        {{ form.as_p }}
        <input type="submit" value="Submit">
    </form>
</body>
</html>

Проблема возникает при проверке:

[введите здесь описание изображения][1]

[1]: https://i.stack.imgur.com/oKD1n.png <- Изображение выпуска

Я не уверен, как проверить информацию для формы создания в "create_db" Я почти уверен, что она проверяет "admin_db", но ни одна из записей не хранится в "admin_db", только в "create_db".

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

Для тех, кто столкнулся с этой проблемой, похоже, что CreateView не работает с внешними ключами и many to many на Django 4.0.5.

.

Простой обходной путь - не использовать Create view и использовать FormView вместо этого и просто создать свою "Форму и добавить вторую базу данных в запрос следующим образом:

from django import forms
from .models import Book, Author, Language, Genre
from django.forms import ModelForm

class BookForm(ModelForm):


    def __init__(self, *args, **kwargs):

        super().__init__(*args, **kwargs)
        # here I force the model's foreign key to the second database
        self.fields['author']=forms.ModelChoiceField(queryset=Author.objects.using('catalog').all())
        self.fields['genre']=forms.ModelMultipleChoiceField(queryset=Genre.objects.using('catalog').all())
        self.fields['language']=forms.ModelChoiceField(queryset=Language.objects.using('catalog').all())

        

    class Meta:
        model = Book
        fields = "__all__"

Тогда я просто использую FormView:

class BookFormView(FormView):
    #basic info
    form_class = BookForm # Connect your form to your view here!
    template_name = 'catalog/book_form.html' # Connect your template!

    def get_success_url(self):
        return reverse_lazy('catalog:book_detail', kwargs={'pk': self.temp.id} ) # Make sure to from django.urls import reverse_lazy


    def form_valid(self, form):
        self.temp = form.save(commit=False) # have to do this first if using a second database
        self.temp.save(using='catalog') # using self to grab the id to redirect on the reverse_lazy
        return super().form_valid(form)

Если кто-нибудь может заставить "CreateView" работать с несколькими базами данных и пожалуйста, поделитесь!

Проблема заключается в том, что вы неправильно указываете DATABASE_ROUTERS. Предполагается, что параметр DATABASE_ROUTERS представляет собой список строк импорта или сам экземпляр маршрутизатора базы данных, тогда как вы передаете список объектов pathlib.Path:

DATABASE_ROUTERS = [
    BASE_DIR / 'routers.db_routers.LibraryRouter'# the "MyApp1Router is the class inside the db_routers file"
]

Вы случайно не получаете никакой ошибки по нескольким причинам, во-первых, реализация Django [GitHub] просто предполагает, что вы передаете ей экземпляр маршрутизатора:

for r in self._routers:
    if isinstance(r, str):
        router = import_string(r)()
    else:
        router = r

Далее, когда дело доходит до фактического использования экземпляра, он просто предполагает, что переданный маршрутизатор не реализует конкретный метод, как видно из code [GitHub]:

for router in self.routers:
    try:
        method = getattr(router, action)
    except AttributeError:
        # If the router doesn't have a method, skip to the next one.
        pass

Проще говоря, ваш маршрутизатор на самом деле не используется, и вам следует обновить настройки, чтобы передать правильную строку импорта:

DATABASE_ROUTERS = [
    'routers.db_routers.LibraryRouter' # the "MyApp1Router is the class inside the db_routers file"
]
Вернуться на верх