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