私はDjangoこのようなモデルを持っています:
class Dummy(models.Model):
...
system = models.CharField(max_length=16)
system
を空にしたり、空白文字を入れたりしないでください。
Djangoでバリデーターを使用する方法を知っています。
しかし、私はこれをデータベースレベルで適用します。
このためのDB制約を作成する最も簡単でDjangoのような方法は何ですか?
私はPostgreSQLを使用しており、他のデータベースをサポートする必要はありません。
最初の問題: Djangoを介したデータベース制約の作成
A) Djangoにはまだこの機能が組み込まれていません。9歳のオープン チケット がありますが、息を止めませんこの長い間続いている何かのために。
編集:リリース2.2(2019年4月)以降、Djangoはデータベースレベルをサポートします チェック制約 。
B)パッケージ Django-db-constraints を調べると、モデルMeta
に制約を定義できます。私はこのパッケージをテストしなかったので、それが実際にどれほど役立つかわかりません。
_# example using this package
class Meta:
db_constraints = {
'price_above_zero': 'check (price > 0)',
}
_
2番目の問題:フィールドsystem
は空にしたり、空白を含めたりしないでください
これを実現するには、check
構文でpostgres
制約を作成する必要があります。私はこれらのオプションを思いつきました:
空白を削除した後、system
の長さが異なるかどうかを確認します。 この答え からのアイデアを使用して、あなたは試すことができます:
_/* this check should only pass if `system` contains no
* whitespaces (`\s` also detects new lines)
*/
check ( length(system) = length(regexp_replace(system, '\s', '', 'g')) )
_
空白カウントが0かどうかを確認します。これを行うには、_regexp_matches
_を使用します。
_/* this check should only pass if `system` contains no
* whitespaces (`\s` also detects new lines)
*/
check ( length(regexp_matches(system, '\s', 'g')) = 0 )
_
length
関数できませんは_regexp_matches
_と一緒に使用されます。後者は_set of text[]
_(配列のセット)を返すためですが、そのセットの要素を今すぐカウントする適切な関数。
最後に、前の問題の両方をまとめると、アプローチは次のようになります。
_class Dummy(models.Model):
# this already sets NOT NULL to the field in the database
system = models.CharField(max_length=16)
class Meta:
db_constraints = {
'system_no_spaces': 'check ( length(system) > 0 AND length(system) = length(regexp_replace(system, "\s", "", "g")) )',
}
_
これにより、フィールドの値がチェックされます。
CharField
はデフォルトで_NOT NULL
_制約を追加します)check
の最初の部分:length(system) > 0
)check
の2番目の部分:空白を置き換えても同じ長さ)それがどのように機能するか、またはこのアプローチに問題や欠点があるかどうかをお知らせください。
Django 2.2は データベースレベルの制約 のサポートを追加しました。新しい CheckConstraint および niqueConstraint クラスにより、カスタムデータベース制約を追加できます。 Meta.constraintsオプション を使用して、制約をモデルに追加します。
システムの検証は次のようになります。
class Dummy(models.Model):
...
system = models.CharField(max_length=16)
class Meta:
constraints = [
CheckConstraint(
check=~Q(system="") & ~Q(system__contains=" "),
name="system_not_blank")
]
カスタムDjango移行を介してCHECK
制約を追加できます。文字列の長さを確認するには、char_length
関数とposition
を使用して、空白が含まれているかどうかを確認します。
Postgres docsからの引用( https://www.postgresql.org/docs/current/static/ddl-constraints.html ):
チェック制約は、最も一般的な制約タイプです。特定の列の値がブール(真の値)式を満たす必要があることを指定できます。
Migarationで任意のsqlを実行するにはRunSQL
操作を使用できます( https://docs.djangoproject.com/en/2.0/ref/migration-operations/#runsql ):
データベース上で任意のSQLを実行できます。Djangoが部分インデックスのように直接サポートしていないデータベースバックエンドのより高度な機能に役立ちます。
空の移行を作成します:
python manage.py makemigrations --empty yourappname
SQLを追加して制約を作成します:
# Generated by Django A.B on YYYY-MM-DD HH:MM
from Django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('yourappname', '0001_initial'),
]
operations = [
migrations.RunSQL('ALTER TABLE appname_dummy ADD CONSTRAINT syslen '
'CHECK (char_length(trim(system)) > 1);',
'ALTER TABLE appname_dummy DROP CONSTRAINT syslen;'),
migrations.RunSQL('ALTER TABLE appname_dummy ADD CONSTRAINT syswh '
'CHECK (position(' ' in trim(system)) = 0);',
'ALTER TABLE appname_dummy DROP CONSTRAINT syswh;')
]
移行を実行します:
python manage.py migrate yourappname
私はあなたの要件に達するために私の答えを変更します。
したがって、DB制約を実行する場合は、次のようにしてください。
import psycopg2
def your_validator():
conn = psycopg2.connect("dbname=YOURDB user=YOURUSER")
cursor = conn.cursor()
query_result = cursor.execute("YOUR QUERY")
if query_result is Null:
# Do stuff
else:
# Other Stuff
次に、pre_save
シグナル。
あなたのmodels.py
ファイル追加、
from Django.db.models.signals import pre_save
class Dummy(models.Model):
...
@staticmethod
def pre_save(sender, instance, *args, **kwargs)
# Of course, feel free to parse args in your def.
your_validator()