Django MPTT: values で tree_id が SELECT されてしまう

Django MPTT: values で tree_id が SELECT されてしまう 仕様である django-mptt の TreeQuerySet で values コールすると SQL に tree_id が追加されているのは、django-mptt がツリー構造を管理するために必要なカラムだからです²。tree_id は、同じツリーに属するノードに同じ値を持ちます²。例えば、以下のようなモデルがあるとします。 1 2 3 4 5 6 7 8 9 from django.db import models from mptt.models import MPTTModel, TreeForeignKey class Category(MPTTModel): name = models.CharField(max_length=50, unique=True) parent = TreeForeignKey('self', on_delete=models.CASCADE, null=True, blank=True, related_name='children') class MPTTMeta: order_insertion_by = ['name'] このとき、以下のように values をコールすると、SQL に tree_id が追加されます²。 ...

2023年4月28日 · 1 分

Django: Template: QuerySetの指定したindexのインスタンスの参照

Django: Template: QuerySet の指定した index のインスタンスの参照 配列ではないので、数値でインデクシングできない {{ e.expenseitem_set.0 }} {# NG #} 1件目, 最後 {{ e.expenseitem_set.first.account_name }} {{ e.expenseitem_set.last.account_name }} テンプレートフィルター 1 2 3 4 5 6 7 from django import template register = template.Library() @register.filter def index(queryset, i): return queryset[i] 1 {% load expenses %} {{ e.expenseitem_set|index:1 }} QuerySet のインクシング list のように -1 などの Python のインデクシングはできない (するには list(queryset) で list に変換する)

2023年4月27日 · 1 分

django: has_permが 常に Falseになる

has_perm が常にFalseになる ## 無効ユーザーだから(User.is_active == False) django.contrib.auth.backends.py: 1 2 3 4 class BaseBackend: def has_perm(self, user_obj, perm, obj=None): # ユーザーが有効の時だけパーミッション判定する return user_obj.is_active and super().has_perm(user_obj, perm, obj=obj) キャッシュがのこっているため Userオブジェクトをクエリし直す

2023年4月26日 · 1 分

MySQL: ERROR 1553 (HY000): Cannot drop index 'uniq_profit': needed in a foreign key constraint

MySQL: ERROR 1553 (HY000): Cannot drop index ‘uniq_profit’: needed in a foreign key constraint MySQL で一意制約が削除できない 1 2 3 UNIQUE KEY `uniq_profit` (`sales_id`,`accounting_status`), CONSTRAINT `costs_profit_sales_id_d56dec15_fk_sales_sales_id` FOREIGN KEY (`sales_id`) REFERENCES `sales_sales` (`id`), CONSTRAINT `costs_profit_chk_1` CHECK ((`fiscal_year` >= 0)) 1 2 ALTER TABLE `costs_profit` DROP FOREIGN KEY `costs_profit_sales_id_d56dec15_fk_sales_sales_id`, DROP INDEX `uniq_profit`; ALTER TABLE `costs_profit` ADD CONSTRAINT `costs_profit_sales_id_d56dec15_fk_sales_sales_id` FOREIGN KEY (`sales_id`) REFERENCES `sales_sales` (`id`); 実行: 1 2 3 4 5 6 7 mysql> ALTER TABLE `costs_profit` DROP FOREIGN KEY `costs_profit_sales_id_d56dec15_fk_sales_sales_id`, DROP INDEX `uniq_profit`; Query OK, 0 rows affected (0.20 sec) Records: 0 Duplicates: 0 Warnings: 0 mysql> ALTER TABLE `costs_profit` ADD CONSTRAINT `costs_profit_sales_id_d56dec15_fk_sales_sales_id` FOREIGN KEY (`sales_id`) REFERENCES `sales_sales` (`id`); Query OK, 16 rows affected (0.17 sec) Records: 16 Duplicates: 0 Warnings: 0 Django Migration の失敗 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ ('partners', '0003_auto_20230426_0908'), ] operations = [ migrations.RemoveConstraint( model_name='partneruserrole', name='uniq_partneruserrole', ), migrations.AddConstraint( model_name='partneruserrole', constraint=models.UniqueConstraint(fields=('partner', 'partneruser', 'permission'), name='uniq_partneruserrole'), ), ] 制約 検索 1 2 3 4 5 6 7 8 9 SELECT DISTINCT A.CONSTRAINT_NAME, A.TABLE_NAME, A.COLUMN_NAME, B.CONSTRAINT_NAME FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE as A JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE as B ON A.TABLE_NAME = B.TABLE_NAME WHERE A.TABLE_NAME = 'partners_partneruserrole' AND B.CONSTRAINT_NAME like 'uniq_%' AND A.CONSTRAINT_NAME not like 'uniq_%' AND A.CONSTRAINT_NAME != 'PRIMARY'; +-----------------------------------------------------------+--------------------------+----------------+----------------------+ | CONSTRAINT_NAME | TABLE_NAME | COLUMN_NAME | CONSTRAINT_NAME | +-----------------------------------------------------------+--------------------------+----------------+----------------------+ | partners_partneruser_partneruser_id_d59f67cc_fk_partners_ | partners_partneruserrole | partneruser_id | uniq_partneruserrole | | partners_partneruser_partner_id_852ab185_fk_partners_ | partners_partneruserrole | partner_id | uniq_partneruserrole | | partners_partneruser_permission_id_031e3996_fk_auth_perm | partners_partneruserrole | permission_id | uniq_partneruserrole | +-----------------------------------------------------------+--------------------------+----------------+----------------------+ 再設定 1 2 3 4 5 6 7 ALTER TABLE `partners_partneruserrole` DROP FOREIGN KEY `partners_partneruser_partneruser_id_d59f67cc_fk_partners_`; ALTER TABLE `partners_partneruserrole` DROP FOREIGN KEY `partners_partneruser_partner_id_852ab185_fk_partners_`; ALTER TABLE `partners_partneruserrole` DROP FOREIGN KEY `partners_partneruser_partner_id_852ab185_fk_partners_`, DROP INDEX `uniq_profit`; ALTER TABLE `partners_partneruserrole` ADD CONSTRAINT `partners_partneruser_partneruser_id_d59f67cc_fk_partners_` FOREIGN KEY (`partneruser_id`) REFERENCES `partners_partneruser` (`id`); ALTER TABLE `partners_partneruserrole` ADD CONSTRAINT `partners_partneruser_partner_id_852ab185_fk_partners_` FOREIGN KEY (`partner_id`) REFERENCES `partners_partner` (`id`); ALTER TABLE `partners_partneruserrole` ADD CONSTRAINT `partners_partneruser_permission_id_031e3996_fk_auth_perm` FOREIGN KEY (`permission_id`) REFERENCES `auth_permission` (`user_ptr_id`); 最終スキーマ 1 2 3 4 5 6 7 8 9 10 11 12 DROP TABLE IF EXISTS `partners_partneruserrole`; CREATE TABLE `partners_partneruserrole` ( `id` bigint NOT NULL AUTO_INCREMENT, .... PRIMARY KEY (`id`), UNIQUE KEY `uniq_partneruserrole` (`partner_id`,`partneruser_id`,`permission_id`), KEY `partners_partneruser_partneruser_id_d59f67cc_fk_partners_` (`partneruser_id`), KEY `partners_partneruser_permission_id_031e3996_fk_auth_perm` (`permission_id`), CONSTRAINT `partners_partneruser_partner_id_852ab185_fk_partners_` FOREIGN KEY (`partner_id`) REFERENCES `partners_partner` (`id`), CONSTRAINT `partners_partneruser_partneruser_id_d59f67cc_fk_partners_` FOREIGN KEY (`partneruser_id`) REFERENCES `partners_partneruser` (`user_ptr_id`), CONSTRAINT `partners_partneruser_permission_id_031e3996_fk_auth_perm` FOREIGN KEY (`permission_id`) REFERENCES `auth_permission` (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

2023年4月26日 · 2 分

django-mptt: tree_idの重複問題

tree_id が重複して登録されていまう問題 多重処理を行っている時に発生することがある模様 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 class TreeManager(models.Manager.from_queryset(TreeQuerySet)): ... @delegate_manager def insert_node(self, node, target, position='last-child', save=False, allow_existing_pk=False, refresh_target=True): if node.pk and not allow_existing_pk and self.filter(pk=node.pk).exists(): raise ValueError(_('Cannot insert a node which has already been saved.')) if target is None: tree_id = self._get_next_tree_id() setattr(node, self.left_attr, 1) setattr(node, self.right_attr, 2) setattr(node, self.level_attr, 0) setattr(node, self.tree_id_attr, tree_id) setattr(node, self.parent_attr, None) ... def _get_next_tree_id(self): max_tree_id = list(self.aggregate(Max(self.tree_id_attr)).values())[0] max_tree_id = max_tree_id or 0 return max_tree_id + 1 ... ロックをかける django-mptt で tree_id の採番に関して、テーブルロックをかけることは可能です。django-mptt は、デフォルトでは、tree_id の採番にテーブルロックをかけていませんが、以下のようにして、テーブルロックをかけることができます³。 ...

2023年4月25日 · 1 分

Django: User: Permissionコードのどれかを保有しているユーザーを検索

Django: User: Permission コードのどれかを持っているユーザーを検索 1 2 3 from operator import or_ from functools import reduce from django.db.models import Q 1 2 3 4 5 6 7 8 9 10 11 12 13 class UserQuerySet(models.QuerySet): def filter_has_perm(self, perm_codeset: List[str]): """ Permission code(List[app_label.codename]) の一覧のどれかを含むユーザーの検索 """ def _query(item): return reduce(or_, [ Q(user_permissions__content_type__app_label=item[0], user_permissions__codename=item[1]), Q(groups__permissions__content_type__app_label=item[0], groups__permissions__codename=item[1]), ]) query = reduce(or_, map(_query, map(lambda i: i.strip().split("."), perm_codeset))) return self.filter(query) 1 2 3 4 5 6 7 8 9 10 11 12 13 class UserFilter(django_filters.FilterSet) def filter_permission_codes__in_csv(self, queryset, name, value): if not value: return queryset values = value.split(",") if isinstance(value, str) else value if len(values) < 1: return queryset return queryset.filter_has_perm(values) permission_codes__in_csv = DF.BaseInFilter( label="パーミッションコード(CSV)", method="filter_permission_codes__in_csv", )

2023年4月23日 · 1 分

Djanog: Permission: app_lablel.codename

Django: Permission: app_label.codename をアノテートする 1 2 3 from django.db.models import F, Value from django.db.models.functions import Concat from django.contrib.auth import models as auth_models 1 2 3 4 5 6 7 class PermissionDef: @classmethod def choices(cls): return auth_models.Permission.objects.annotate( code=Concat("content_type__app_label", Value("."), "codename") ).values_list("code", "name") ただし、件数が大きいので, フィールドのchoices に使うのは現実的ではない

2023年4月23日 · 1 分

DRF: Userを所属グループで検索

django-filters: Userを所属グループで検索 Django REST Frameworkとdjango-filterを使って、Userが所属しているGroupを複数指定して検索することはできます。 以下のように、django-filter.FilterSetを継承したフィルタセットを定義し、filters.ModelMultipleChoiceFilterを使って、Userが所属しているGroupを複数指定することができます。 1 2 3 4 5 6 7 8 9 10 11 12 13 from django.contrib.auth.models import User, Group import django_filters class UserFilter(django_filters.FilterSet): groups = django_filters.ModelMultipleChoiceFilter( field_name='groups__name', to_field_name='name', queryset=Group.objects.all() ) class Meta: model = User fields = ['username', 'groups'] 上記の例では、Userモデルに対するフィルタセットを定義しています。groupsフィールドには、django_filters.ModelMultipleChoiceFilterを使って、Userが所属しているGroupを複数指定することができます。field_nameには、Userモデルのgroups__nameフィールドを指定し、to_field_nameには、Groupモデルのnameフィールドを指定しています。 ...

2023年4月23日 · 1 分

Python: モジュールの実装パス

モジュール名からファイルを見つける Pythonのモジュール名が与えられた時、そのモジュールの実装されているファイルのパスを調べるには、以下のようにします。 1 2 3 4 5 6 7 import importlib.util def get_module_path(module_name): spec = importlib.util.find_spec(module_name) if spec is None: return None return spec.origin 上記のように、importlib.util.find_spec()関数を使って、モジュール名からspecオブジェクトを取得し、spec.origin属性を参照することで、モジュールが実装されているファイルのパスを取得することができます。 ソース: Bing との会話 2023/4/23 (1) 6. モジュール — Python 3.11.3 ドキュメント. https://docs.python.org/ja/3/tutorial/modules.html. (2) Pythonで独自モジュールのディレクトリパスを通す方法 …. https://lightgauge.net/language/python/add-module-path. (3) 【Python】パッケージ・モジュールの検索と import - Qiita. https://qiita.com/shinsa82/items/5004a3ea63594f20190d. Djangoのアプリケーションの場合 1 2 3 4 5 6 7 8 from django.apps import apps class Def: @property def path(self): app_label = self.__module__.split(".")[0] conf = apps.get_app_config(app_label) return conf.path

2023年4月23日 · 1 分

Django: User ログインシグナル

Django: User ログイン ログインシグナル Djangoでログインした時のイベントをシグナルで受け取る方法については、Djangoの公式ドキュメントに詳しく記載されています¹。以下のように、django.contrib.auth.signals.user_logged_inシグナルを使用して、ログイン時に実行する関数を定義し、receiver()デコレーターでシグナルを受信する関数を登録します。 1 2 3 4 5 6 7 from django.contrib.auth.signals import user_logged_in from django.dispatch import receiver @receiver(user_logged_in) def user_logged_in_callback(sender, request, user, **kwargs): # ログイン時に実行する処理 pass このように、シグナルを使用することで、ログイン時に任意の処理を実行することができます。 ソース: Bing との会話 2023/4/21 (1) 「超簡単」Djangoでログインページの実装 - Qiita. https://qiita.com/Yuji-Sakata0110/items/3722fa651e02eafc4d01. (2) 【Django】ログイン・ログアウト機能の実装 - Qiita. https://qiita.com/onishi_820/items/c69ebb6a4e34da0f34d4. (3) Django パスワード試行回数ロックとランダムかつ有効期限付き …. https://qiita.com/startours777/items/5b1fc415e047a2044129.

2023年4月21日 · 1 分