注文は複数の注文に分割できますが、元の注文との関係を維持する必要があるため、自動インクリメントの主キーとそれ自体への外部キーを使用して、webshopアプリケーションに注文のモデルを用意しました。
class Order(models.Model):
ordernumber = models.AutoField(primary_key=True)
parent_order = models.ForeignKey('self', null=True, blank=True, related_name='child_orders')
# .. other fields not relevant here
管理サイトのOrderAdminクラスを登録しました。詳細ビューでは、fieldsets
属性にparent_order
を含めました。もちろん、デフォルトではすべての注文が選択ボックスにリストされますが、これは望ましい動作ではありません。代わりに、親注文がない注文(つまり、別の注文から分割されていない、parent_order
はNULL /なし)の場合、注文は表示されません。分割された注文の場合、これは単一の親注文のみを表示します。
クエリセットは内部でフィルター処理できるため、これに最適なように見えるかなり新しいModelAdminメソッドformfield_for_foreignkey
があります。注文#11208から分割された注文#11234の詳細ビューを見ているところを想像してみてください。コードは以下です
def formfield_for_foreignkey(self, db_field, request, **kwargs):
if db_field.name == 'parent_order':
# kwargs["queryset"] = Order.objects.filter(child_orders__ordernumber__exact=11234)
return db_field.formfield(**kwargs)
return super(OrderAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
コメント付きの行は、Pythonシェルで実行すると機能し、#11234の注文#11208およびそれから分割された可能性のある他のすべての注文を含む単一アイテムクエリセットを返します。
もちろん、注文番号をハードコードすることはできません。詳細ページを表示している注文インスタンスのordernumber
フィールドへの参照が必要です。このような:
kwargs["queryset"] = Order.objects.filter(child_orders__ordernumber__exact=?????)
?????を置き換えるための有効な方法が見つかりませんでした。 「現在の」Orderインスタンスへの参照を使用して、かなり深く掘り下げました。 self
内部formfield_for_foreignkey
はModelAdminインスタンスを参照し、model
属性はありますが、注文モデルインスタンスではありません(ModelBase参照です。self.model()が返します)インスタンスですが、その注文番号はNoneです)。
解決策の1つは、request.path(/ admin/orders/order/11234 /)から注文番号を取得することですが、これは実際には醜いです。もっと良い方法があればと思います。
管理クラスではなく、ModelFormを変更することで、少し異なる方法でこれに取り組む必要があると思います。このようなもの:
class OrderForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(OrderForm, self).__init__(*args, **kwargs)
self.fields['parent_order'].queryset = Order.objects.filter(
child_orders__ordernumber__exact=self.instance.pk)
class OrderAdmin(admin.ModelAdmin):
form = OrderForm
この方法でインラインクラスをモデル化しました。インラインデータをフィルター処理するために親フォームIDを取得する方法は少し醜いですが、機能します。親フォームから会社別にユニットをフィルタリングします。
元のコンセプトはここで説明されています http://www.stereoplex.com/blog/filtering-dropdown-lists-in-the-Django-admin
class CompanyOccupationInline(admin.TabularInline):
model = Occupation
# max_num = 1
extra = 0
can_delete = False
formset = RequiredInlineFormSet
def formfield_for_dbfield(self, field, **kwargs):
if field.name == 'unit':
parent_company = self.get_object(kwargs['request'], Company)
units = Unit.objects.filter(company=parent_company)
return forms.ModelChoiceField(queryset=units)
return super(CompanyOccupationInline, self).formfield_for_dbfield(field, **kwargs)
def get_object(self, request, model):
object_id = resolve(request.path).args[0]
try:
object_id = int(object_id)
except ValueError:
return None
return model.objects.get(pk=object_id)
上記のErwin Juliusからの回答はうまくいきましたが、「get_object」という名前はDjango関数と競合するため、「my_get_object」という名前を付けます。
class CompanyOccupationInline(admin.TabularInline):
model = Occupation
# max_num = 1
extra = 0
can_delete = False
formset = RequiredInlineFormSet
def formfield_for_dbfield(self, field, **kwargs):
if field.name == 'unit':
parent_company = self.my_get_object(kwargs['request'], Company)
units = Unit.objects.filter(company=parent_company)
return forms.ModelChoiceField(queryset=units)
return super(CompanyOccupationInline, self).formfield_for_dbfield(field, **kwargs)
def my_get_object(self, request, model):
object_id = request.META['PATH_INFO'].strip('/').split('/')[-1]
try:
object_id = int(object_id)
except ValueError:
return None
return model.objects.get(pk=object_id)
他の人の答えに「返信」しないように言われましたが、まだ「返信」することはできません。しばらく探していたので、他の人の役に立つと思います。私もまだ賛成投票することはできません。