Как исключить неактивные (мягкие удаленные) поля внешнего ключа в Django Model Forms? [Дубликат]

Решения здесь предназначены только для доступа к глубоко вложенным ключам. Мне нужен был для доступа, добавления, изменения и удаления ключей. Вот что я придумал:

var deepAccessObject = function(object, path_to_key, type_of_function, value){
    switch(type_of_function){
        //Add key/modify key
        case 0: 
            if(path_to_key.length === 1){
                if(value)
                    object[path_to_key[0]] = value;
                return object[path_to_key[0]];
            }else{
                if(object[path_to_key[0]])
                    return deepAccessObject(object[path_to_key[0]], path_to_key.slice(1), type_of_function, value);
                else
                    object[path_to_key[0]] = {};
            }
            break;
        //delete key
        case 1:
            if(path_to_key.length === 1){
                delete object[path_to_key[0]];
                return true;
            }else{
                if(object[path_to_key[0]])
                    return deepAccessObject(object[path_to_key[0]], path_to_key.slice(1), type_of_function, value);
                else
                    return false;
            }
            break;
        default:
            console.log("Wrong type of function");
    }
};
  • path_to_key: путь в массиве. Вы можете заменить его на string_path.split(".").
  • type_of_function: 0 для доступа (не передавать любое значение в value), 0 для добавления и изменения. 1 для удаления.
194
задан Tom 15 November 2008 в 02:21
поделиться

7 ответов

ForeignKey представлен django.forms.ModelChoiceField, который является ChoiceField, выбор которого является модель QuerySet. См. Ссылку для ModelChoiceField .

Итак, укажите QuerySet для атрибута поля queryset. Зависит от того, как ваша форма построена. Если вы создадите явную форму, у вас будут поля с именами напрямую.

form.rate.queryset = Rate.objects.filter(company_id=the_company.id)

Если вы берете объект ModelForm по умолчанию, form.fields["rate"].queryset = ...

Это делается явно в представлении. Нет взлома.

209
ответ дан S.Lott 24 August 2018 в 00:14
поделиться
  • 1
    Хорошо, это звучит многообещающе. Как получить доступ к соответствующему полевому объекту? form.company.QuerySet = Rate.objects.filter (company_id = the_company.id)? или через словарь? – Tom 15 November 2008 в 03:43
  • 2
    Хорошо, спасибо за расширение примера, но мне кажется, что мне нужно использовать form.fields [& quot; rate & quot;]. Queryset, чтобы избежать & quot; Объект ClientForm не имеет атрибута 'rate' & quot ;, я чего-то не хватает? (и ваш пример должен быть form.rate.queryset, чтобы быть последовательным тоже.) – Tom 15 November 2008 в 04:36
  • 3
    Не было бы лучше установить набор запросов полей в методе __init__ формы? – Lakshman Prasad 3 August 2009 в 22:03
  • 4
    @SLott последний комментарий некорректен (или мой сайт не должен работать :). Вы можете заполнить данные валидации, используя метод super (...) .__ init__ в вашем переопределенном методе. Если вы делаете несколько из этих изменений в запросе, их намного более элегантно, чтобы упаковать их, переопределив метод init . – michael 7 August 2009 в 05:53
  • 5
    @Slott приветствует, я добавил ответ, поскольку для объяснения потребуется более 600 символов. Даже если этот вопрос устарел, он получает высокую оценку google. – michael 7 August 2009 в 14:19

Чтобы сделать это с помощью общего представления, такого как CreateView ...

class AddPhotoToProject(CreateView):
    """
    a view where a user can associate a photo with a project
    """
    model = Connection
    form_class = CreateConnectionForm


    def get_context_data(self, **kwargs):
        context = super(AddPhotoToProject, self).get_context_data(**kwargs)
        context['photo'] = self.kwargs['pk']
        context['form'].fields['project'].queryset = Project.objects.for_user(self.request.user)
        return context
    def form_valid(self, form):
        pobj = Photo.objects.get(pk=self.kwargs['pk'])
        obj = form.save(commit=False)
        obj.photo = pobj
        obj.save()

        return_json = {'success': True}

        if self.request.is_ajax():

            final_response = json.dumps(return_json)
            return HttpResponse(final_response)

        else:

            messages.success(self.request, 'photo was added to project!')
            return HttpResponseRedirect(reverse('MyPhotos'))

, самая важная часть этого ...

    context['form'].fields['project'].queryset = Project.objects.for_user(self.request.user)

, read мой пост здесь

15
ответ дан BoltClock 24 August 2018 в 00:14
поделиться

Более общедоступным способом является вызов get_form в классах Admin. Он также работает и для полей без базы данных. Например, здесь у меня есть поле «_terminal_list» в форме, которое может использоваться в особых случаях для выбора нескольких элементов терминала из get_list (request), а затем фильтрации на основе request.user:

class ChangeKeyValueForm(forms.ModelForm):  
    _terminal_list = forms.ModelMultipleChoiceField( 
queryset=Terminal.objects.all() )

    class Meta:
        model = ChangeKeyValue
        fields = ['_terminal_list', 'param_path', 'param_value', 'scheduled_time',  ] 

class ChangeKeyValueAdmin(admin.ModelAdmin):
    form = ChangeKeyValueForm
    list_display = ('terminal','task_list', 'plugin','last_update_time')
    list_per_page =16

    def get_form(self, request, obj = None, **kwargs):
        form = super(ChangeKeyValueAdmin, self).get_form(request, **kwargs)
        qs, filterargs = Terminal.get_list(request)
        form.base_fields['_terminal_list'].queryset = qs
        return form
0
ответ дан FloatingKiwi 24 August 2018 в 00:14
поделиться

Это просто и работает с Django 1.4:

class ClientAdminForm(forms.ModelForm):
    def __init__(self, *args, **kwargs):
        super(ClientAdminForm, self).__init__(*args, **kwargs)
        # access object through self.instance...
        self.fields['base_rate'].queryset = Rate.objects.filter(company=self.instance.company)

class ClientAdmin(admin.ModelAdmin):
    form = ClientAdminForm
    ....

Вам не нужно указывать это в классе формы, но можете делать это непосредственно в ModelAdmin, поскольку Django уже включает это встроенный метод в ModelAdmin (из документов):

ModelAdmin.formfield_for_foreignkey(self, db_field, request, **kwargs)¶
'''The formfield_for_foreignkey method on a ModelAdmin allows you to 
   override the default formfield for a foreign keys field. For example, 
   to return a subset of objects for this foreign key field based on the
   user:'''

class MyModelAdmin(admin.ModelAdmin):
    def formfield_for_foreignkey(self, db_field, request, **kwargs):
        if db_field.name == "car":
            kwargs["queryset"] = Car.objects.filter(owner=request.user)
        return super(MyModelAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)

. Даже более удобный способ сделать это (например, при создании интерфейса администратора администратора, к которому пользователи могут обращаться) - подклассировать ModelAdmin, а затем измените приведенные ниже методы. Конечным результатом является пользовательский интерфейс, который ТОЛЬКО показывает им контент, связанный с ними, позволяя вам (суперпользователю) видеть все.

Я переопределил четыре метода, первые два сделали это невозможно для пользователя что-либо удалить, а также удаляет кнопки удаления с сайта администратора.

Третье переопределение фильтрует любой запрос, содержащий ссылку (в примере «пользователь» или «дикобраз» (как иллюстрация).

Последнее переопределение фильтрует любое поле внешней клавиши в модели для фильтрации доступных вариантов, таких же, как и для базового набора запросов.

Таким образом, вы можете создать простой для управления фронт-админ-сайт, который позволяет пользователям общаться со своими собственными объектами, а вы не нужно помнить, чтобы вводить специальные фильтры ModelAdmin, о которых мы говорили выше.

class FrontEndAdmin(models.ModelAdmin):
    def __init__(self, model, admin_site):
        self.model = model
        self.opts = model._meta
        self.admin_site = admin_site
        super(FrontEndAdmin, self).__init__(model, admin_site)

удалить кнопки «delete»:

    def get_actions(self, request):
        actions = super(FrontEndAdmin, self).get_actions(request)
        if 'delete_selected' in actions:
            del actions['delete_selected']
        return actions

предотвращает разрешение на удаление

    def has_delete_permission(self, request, obj=None):
        return False

фильтрует объекты, которые можно просмотреть на сайте администратора:

    def get_queryset(self, request):
        if request.user.is_superuser:
            try:
                qs = self.model.objects.all()
            except AttributeError:
                qs = self.model._default_manager.get_queryset()
            return qs

        else:
            try:
                qs = self.model.objects.all()
            except AttributeError:
                qs = self.model._default_manager.get_queryset()

            if hasattr(self.model, ‘user’):
                return qs.filter(user=request.user)
            if hasattr(self.model, ‘porcupine’):
                return qs.filter(porcupine=request.user.porcupine)
            else:
                return qs

выбирает фильтры для всех полей foreignkey на сайте admin:

    def formfield_for_foreignkey(self, db_field, request, **kwargs):
        if request.employee.is_superuser:
            return super(FrontEndAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)

        else:
            if hasattr(db_field.rel.to, 'user'):
                kwargs["queryset"] = db_field.rel.to.objects.filter(user=request.user)
            if hasattr(db_field.rel.to, 'porcupine'):
                kwargs["queryset"] = db_field.rel.to.objects.filter(porcupine=request.user.porcupine)
            return super(ModelAdminFront, self).formfield_for_foreignkey(db_field, request, **kwargs)
39
ответ дан neil.millikin 24 August 2018 в 00:14
поделиться
  • 1
    И я должен добавить, что это хорошо работает как универсальная пользовательская форма для нескольких modeladmins с похожими ссылочными полями, представляющими интерес. – nemesisfixx 27 July 2013 в 11:39
  • 2
    Это лучший ответ, если вы используете Django 1.4+ – Rick Westera 26 March 2015 в 01:49

Итак, я действительно пытался это понять, но кажется, что Django все еще не делает этого очень простым. Я не настолько глупый, но я просто не вижу какого-либо (несколько) простого решения.

Я считаю, что в целом довольно уродливо переопределять виды Admin для такого рода вещей, и каждый пример, который я нахожу никогда полностью не применимым к представлениям администратора.

Это такое распространенное обстоятельство с моделями, которые я делаю, что я нахожу это ужасным, что нет очевидного решения этого ...

У меня есть эти классы:

# models.py
class Company(models.Model):
    # ...
class Contract(models.Model):
    company = models.ForeignKey(Company)
    locations = models.ManyToManyField('Location')
class Location(models.Model):
    company = models.ForeignKey(Company)

Это создает проблему при настройке администратора для компании, поскольку у него есть встроенные параметры для контракта и местоположения, а параметры m2m для Контракта для местоположения не фильтруются должным образом в соответствии с компанией, которую вы сейчас редактируете.

Короче говоря, мне понадобится какая-то опция администратора, чтобы сделать что-то вроде этого:

# admin.py
class LocationInline(admin.TabularInline):
    model = Location
class ContractInline(admin.TabularInline):
    model = Contract
class CompanyAdmin(admin.ModelAdmin):
    inlines = (ContractInline, LocationInline)
    inline_filter = dict(Location__company='self')

В конечном счете мне было бы безразлично, если процесс фильтрации был размещен на базе CompanyAdmin, или если он был помещен в ContractInline. (Размещение его на inline имеет больше смысла, но это затрудняет ссылку базового контракта как «я».)

Есть ли кто-нибудь, кто знает что-то столь же прямое, как этот крайне необходимый ярлык? Назад, когда я сделал администраторов PHP для такого рода вещей, это считалось базовой функциональностью! Фактически, он всегда был автоматическим и должен был быть отключен, если вы действительно этого не хотели!

2
ответ дан Tim 24 August 2018 в 00:14
поделиться

В дополнение к ответу С.Лотта и, как упоминается в комментариях к Guru, его можно добавить фильтры выбора запроса, переопределив функцию ModelForm.__init__. (Это может быть легко применимо к обычным формам), это может помочь в повторном использовании и сохраняет функцию обзора в порядке.

class ClientForm(forms.ModelForm):
    def __init__(self,company,*args,**kwargs):
        super (ClientForm,self ).__init__(*args,**kwargs) # populates the post
        self.fields['rate'].queryset = Rate.objects.filter(company=company)
        self.fields['client'].queryset = Client.objects.filter(company=company)

    class Meta:
        model = Client

def addclient(request, company_id):
        the_company = get_object_or_404(Company, id=company_id)

        if request.POST:
            form = ClientForm(the_company,request.POST)  #<-- Note the extra arg
            if form.is_valid():
                form.save()
                return HttpResponseRedirect(the_company.get_clients_url())
        else:
            form = ClientForm(the_company)

        return render_to_response('addclient.html', 
                                  {'form': form, 'the_company':the_company})

Это может быть полезно для повторного использования, если у вас есть общие фильтры, необходимые для многих моделей (обычно я объявляю абстрактный класс формы). Например,

class UberClientForm(ClientForm):
    class Meta:
        model = UberClient

def view(request):
    ...
    form = UberClientForm(company)
    ...

#or even extend the existing custom init
class PITAClient(ClientForm):
    def __init__(company, *args, **args):
        super (PITAClient,self ).__init__(company,*args,**kwargs)
        self.fields['support_staff'].queryset = User.objects.exclude(user='michael')

Кроме этого, я просто переписываю материал блога Django, из которого есть много хороших.

119
ответ дан Tom 24 August 2018 в 00:14
поделиться
  • 1
    В вашем первом фрагменте кода есть опечатка, вы определяете аргументы дважды в __init __ () вместо args и kwargs. – tpk 24 January 2010 в 18:55
  • 2
    приветствия, которые обновлены – michael 24 January 2010 в 23:15
  • 3
    Мне нравится этот ответ лучше, я думаю, что чистить инкапсулировать логику инициализации формы в классе формы, а не в методе представления. Ура! – Symmetric 13 October 2012 в 20:49

Если вы еще не создали форму и хотите изменить набор запросов, вы можете сделать:

formmodel.base_fields['myfield'].queryset = MyModel.objects.filter(...)

Это очень полезно, когда вы используете общие представления!

4
ответ дан Wtower 24 August 2018 в 00:14
поделиться
Другие вопросы по тегам:

Похожие вопросы: