Pergunta

Eu tenho um modelo para ordens em um aplicativo de loja virtual, com uma chave primária auto-incremento e uma chave estrangeira para si, uma vez que as encomendas podem ser divididos em várias ordens, mas a relação com a ordem original deve ser mantida.

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

Eu tenho registrado uma classe OrderAdmin para o site de administração. Para a vista de detalhe, eu incluí parent_order no atributo fieldsets. Claro que, por padrão este Listas de todas as ordens em uma caixa de seleção, mas este não é o comportamento desejado. Em vez disso, para as encomendas que não têm uma ordem de pai (ou seja, não têm sido dividida de outra ordem; parent_order é NULL / None), nenhuma ordem deve ser exibido. Para encomendas que foram divididos, isso só deve apresentar a ordem de mãe solteira.

Há um bastante novo método ModelAdmin disponíveis, formfield_for_foreignkey, que parece perfeito para isso, já que o queryset podem ser filtrados dentro dela. Imagine que nós estamos olhando para a vista da ordem # 11234, que foi dividido a partir da ordem # 11208 detalhes. O código está abaixo

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)

A linha comentou funciona quando executado em um shell Python, retornando um queryset-item único contendo fim # 11208 para 11234 # e todas as outras ordens que podem ter sido dividido com isso.

É claro que não pode codificar o número de ordem lá. Precisamos de uma referência ao campo ordernumber da instância ordem cuja página de detalhes que estamos olhando. Como esta:

kwargs["queryset"] = Order.objects.filter(child_orders__ordernumber__exact=?????)

Eu encontrei nenhuma maneira de trabalho para substituir ????? com uma referência à instância "atual" Ordem, e eu cavou bem fundo. self dentro formfield_for_foreignkey refere-se ao exemplo ModelAdmin, e ao mesmo tempo que tem um atributo model, que não é o caso do modelo de ordem (que é uma referência ModelBase; self.model () retorna um exemplo, mas a sua NÚMERO DO PEDIDO é Nenhum).

Uma solução poderia ser a de puxar o número de ordem de request.path (/ admin / encomendas / ordem / 11234 /), mas que é realmente feio. Eu realmente gostaria que há uma maneira melhor.

Foi útil?

Solução

Eu acho que você pode precisar de abordar isso de uma maneira um pouco diferente - modificando o ModelForm, ao invés da classe admin. Algo parecido com isto:

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

Outras dicas

eu modelei minha classe em linha desta forma. É um pouco feio de como ele recebe o formulário pai ID de dados em linha filtro, mas funciona. Ele filtra unidades por empresa do formulário pai.

O conceito original é explicado aqui 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)

A resposta acima Erwin Julius trabalhou para mim, se eu achei que o nome "get_object" conflitos com a função de Django então nomear o "my_get_object" função.

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)

me disse que não "responder" às respostas dos outros, mas eu não estou autorizado a 'resposta' ainda, e eu tenho procurado isso por um tempo, espero que isso vai ser útil para outras pessoas. Eu também não estou autorizado a upvote ainda ou I totalmente faria!

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top