আমি কীভাবে জ্যাঙ্গো মডেলফর্মে বিদেশী কী পছন্দগুলি ফিল্টার করব?


227

বলুন আমার মধ্যে নিম্নলিখিতগুলি রয়েছে models.py:

class Company(models.Model):
   name = ...

class Rate(models.Model):
   company = models.ForeignKey(Company)
   name = ...

class Client(models.Model):
   name = ...
   company = models.ForeignKey(Company)
   base_rate = models.ForeignKey(Rate)

অর্থাত একাধিক হয় Companiesপ্রতিটি একটি সীমার হচ্ছে Ratesএবং Clients। প্রত্যেকের Clientএকটি বেস থাকা উচিত Rateযা তার পিতামাতার থেকে বেছে নেওয়া হয় Company's Rates, অন্যটি নয় Company's Rates

একটি যুক্ত করার জন্য একটি ফর্ম তৈরি করার সময় Client, আমি Companyপছন্দগুলি অপসারণ করতে চাই (এটি যেমন Companyপৃষ্ঠায় একটি "ক্লায়েন্ট যুক্ত করুন" বোতামের মাধ্যমে ইতিমধ্যে নির্বাচন করা হয়েছে ) এবং Rateপছন্দগুলিও এতে সীমাবদ্ধ করুন Company

জ্যাঙ্গো ০.০ এ কীভাবে যাব?

আমার বর্তমান forms.pyফাইলটি এই মুহুর্তে কেবলমাত্র বয়লারপ্লেট:

from models import *
from django.forms import ModelForm

class ClientForm(ModelForm):
    class Meta:
        model = Client

এবং views.pyএটিও প্রাথমিক:

from django.shortcuts import render_to_response, get_object_or_404
from models import *
from forms import *

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

    if request.POST:
        form = ClientForm(request.POST)
        if form.is_valid():
            form.save()
            return HttpResponseRedirect(the_company.get_clients_url())
    else:
        form = ClientForm()

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

জ্যাঙ্গো 0.96-তে আমি টেমপ্লেটটি রেন্ডার করার আগে নীচের মতো কিছু করে এই হ্যাক করতে সক্ষম হয়েছি:

manipulator.fields[0].choices = [(r.id,r.name) for r in Rate.objects.filter(company_id=the_company.id)]

ForeignKey.limit_choices_toপ্রতিশ্রুতিবদ্ধ বলে মনে হচ্ছে তবে কীভাবে পাস করতে হবে the_company.idতা আমি জানি না এবং এটি কীভাবে অ্যাডমিন ইন্টারফেসের বাইরে কাজ করবে তা আমি পরিষ্কার নয়।

ধন্যবাদ। (এটি একটি দুর্দান্ত বেসিক অনুরোধের মতো বলে মনে হচ্ছে তবে আমার যদি নতুন কিছু ডিজাইন করা উচিত তবে আমি পরামর্শের জন্য উন্মুক্ত))


"সীমাবদ্ধতা_ছবি_কে" ইঙ্গিত দেওয়ার জন্য আপনাকে ধন্যবাদ। এটি আপনার প্রশ্নের সমাধান করে না, তবে আমার :-) ডক্স: ডকস.ডজ্যাঙ্গোপ্রজেক্ট
en

উত্তর:


243

ফরেনকি-কে উপস্থাপন করেছেন django.forms.ModelChoiceField, যা একটি চয়েসফিল্ড যার পছন্দগুলি মডেল কোয়েরীসেট। মডেলচয়েসফিল্ডের জন্য রেফারেন্সটি দেখুন ।

সুতরাং, ক্ষেত্রের querysetবৈশিষ্ট্যে একটি ক্যোয়ারীসেট সরবরাহ করুন । আপনার ফর্মটি কীভাবে নির্মিত তা নির্ভর করে। আপনি যদি একটি সুস্পষ্ট ফর্ম তৈরি করেন তবে আপনার সরাসরি নাম ক্ষেত্র থাকবে।

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

আপনি যদি ডিফল্ট মডেলফর্ম অবজেক্ট নেন, form.fields["rate"].queryset = ...

এটি দৃশ্যে স্পষ্টভাবে করা হয়। আশেপাশে হ্যাকিং নেই।


ঠিক আছে, আশাবাদী মনে হচ্ছে। আমি কীভাবে প্রাসঙ্গিক ফিল্ড অবজেক্ট অ্যাক্সেস করব? form.company.QuerySet = হার.objects.filter (Company_id = the_company.id)? বা একটি অভিধানের মাধ্যমে?
টম

1
ঠিক আছে, উদাহরণটি প্রসারণ করার জন্য ধন্যবাদ, তবে আমি ফর্ম ফিল্ডগুলি ব্যবহার করতে হবে বলে মনে হচ্ছে [ক্ষেত্রগুলি "" রেট "]" (এবং আপনার উদাহরণটিও নিয়মিত হতে হবে form.rate.queryset।)
টম

8
ফর্মের __init__পদ্ধতিতে ক্ষেত্রগুলির ক্যোরিসেটটি সেট করা ভাল না ?
লক্ষ্মণ প্রসাদ 21

1
@ স্লট শেষ মন্তব্যটি সঠিক নয় (বা আমার সাইটটি কাজ করা উচিত নয়)। আপনি আপনার ওভাররাইড পদ্ধতিতে সুপার (...) .__ init__ কল ব্যবহার করে বৈধতা ডেটা পপুলেট করতে পারেন। আপনি যদি এই বেশ কয়েকটি ক্যোরিসেট তৈরি করে থাকেন তবে init পদ্ধতিটি ওভাররাইড করে প্যাকেজ করার জন্য এটি আরও অনেক মার্জিত ।
মাইকেল 25 ই

3
@ স্লট চিয়ার্স, আমি একটি উত্তর যুক্ত করেছি কারণ এটি ব্যাখ্যা করতে 600 টিরও বেশি অক্ষর লাগবে। এই প্রশ্নটি পুরান হলেও এটি একটি উচ্চ গুগল স্কোর পাচ্ছে।
মাইকেল 13

135

এস.লোটের উত্তর ছাড়াও এবং মন্তব্যে গুরু হিসাবে উল্লেখ করা হিসাবে, ফাংশনটিকে ওভাররাইড করে ক্যোয়ারেট ফিল্টার যুক্ত করা সম্ভব 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')

এর বাইরে আমি জ্যাঙ্গো ব্লগের উপাদানগুলিকে কেবল বিশ্রাম দিচ্ছি যার মধ্যে অনেকগুলি ভাল রয়েছে।


আপনার প্রথম কোড স্নিপেটে একটি টাইপ রয়েছে, আপনি আরোগুলি এবং কোয়ার্গসের পরিবর্তে __init __ () এ দু'বার আরগগুলি সংজ্ঞায়িত করছেন।
tpk

6
আমি এই উত্তরটি আরও ভাল পছন্দ করি, আমি মনে করি এটি ভিউ পদ্ধতির পরিবর্তে ফর্ম ক্লাসে ফর্ম আরম্ভের যুক্তি সজ্জিত করা পরিষ্কার। চিয়ার্স!
প্রতিসম

44

এটি সহজ, এবং জাঙ্গো ১.৪ এর সাথে কাজ করে:

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.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)

এটি করার একটি এমনকি দ্বিগুণ উপায় (উদাহরণস্বরূপ, ব্যবহারকারীরা অ্যাক্সেস করতে পারে এমন ফ্রন্ট-এন্ড অ্যাডমিন ইন্টারফেস তৈরির ক্ষেত্রে) মডেলএডমিনকে সাবক্লাস করতে হবে এবং তারপরে নীচের পদ্ধতিগুলিকে পরিবর্তন করতে হবে। নেট রেজাল্ট হ'ল একটি ইউজার ইন্টারফেস যা কেবলমাত্র তাদের সাথে সম্পর্কিত এমন সামগ্রী দেখায়, যখন আপনাকে (একজন অতি ব্যবহারকারী) সমস্ত কিছু দেখার অনুমতি দেয়।

আমি চারটি পদ্ধতি ওভাররাইড করেছি, প্রথম দুটি ব্যবহারকারীর পক্ষে যে কোনও কিছু মুছে ফেলা অসম্ভব করে তোলে এবং এটি অ্যাডমিন সাইট থেকে মুছুন বোতামগুলিও সরিয়ে দেয়।

তৃতীয় ওভাররাইড কোনও ক্যোয়ারী ফিল্টার করে যা কোনও রেফারেন্স রয়েছে (উদাহরণস্বরূপ 'ব্যবহারকারী' বা 'পর্কুইকাইন' (ঠিক উদাহরণ হিসাবে)।

মৌলিক ক্যোরিসেটের মতো উপলভ্য পছন্দগুলি ফিল্টার করতে শেষ ওভাররাইড মডেলটির কোনও বিদেশী ক্ষেত্র ফিল্টার করে।

এইভাবে, আপনি সামনের-সামনের অ্যাডমিন সাইট পরিচালনা করার জন্য একটি সহজ উপস্থাপন করতে পারেন যা ব্যবহারকারীদের তাদের নিজস্ব বস্তুগুলির সাথে গোলযোগ করতে দেয় এবং আপনার উপরে আমরা যে নির্দিষ্ট মডেলএডমিন ফিল্টারগুলি নিয়ে আলোচনা করেছি তা টাইপ করতে হবে না।

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)

'মুছুন' বোতামগুলি সরান:

    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

প্রশাসক সাইটে সমস্ত বিদেশি ক্ষেত্রের জন্য পছন্দগুলি ফিল্টার করে:

    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)

1
এবং আমার যুক্ত করা উচিত যে এটি আগ্রহের অনুরূপ রেফারেন্স ক্ষেত্রগুলির সাথে একাধিক মোডাল্ডমিনগুলির জন্য জেনেরিক কাস্টম ফর্মের পাশাপাশি কাজ করে।
nemesisfixx

আপনি জ্যাঙ্গো ১.৪+ ব্যবহার করছেন
রিক ওয়েস্টেরার

16

জেনেরিক ভিউয়ের সাথে এটি করতে, যেমন ক্রিয়েটভিউ ...

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)

, আমার পোস্ট এখানে পড়ুন


4

আপনি যদি ফর্মটি তৈরি না করে থাকেন এবং ক্যোরিসেটটি পরিবর্তন করতে চান তবে আপনি করতে পারেন:

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

আপনি জেনেরিক ভিউগুলি ব্যবহার করার সময় এটি বেশ কার্যকর!


2

সুতরাং, আমি সত্যিই এটি বোঝার চেষ্টা করেছি, তবে মনে হয় জ্যাঙ্গো এখনও এটিকে খুব সোজা করে তোলে না। আমি সমস্ত বোবা নই, তবে আমি কোনও (কিছুটা) সহজ সমাধান দেখতে পাচ্ছি না।

এই ধরণের জিনিসটির জন্য অ্যাডমিনের দৃষ্টিভঙ্গিগুলিকে ওভাররাইড করতে আমি সাধারণত দেখতে বেশ কুৎসিত বলে মনে করি এবং আমি যে উদাহরণ খুঁজে পাই তা কখনও অ্যাডমিন ভিউগুলিতে পুরোপুরি প্রযোজ্য না।

মডেলগুলির সাথে এটি এমন একটি সাধারণ পরিস্থিতি যে আমি এটি বিস্মিত মনে করি যে এর কোনও সুস্পষ্ট সমাধান নেই ...

আমি এই ক্লাস পেয়েছি:

# 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)

এটি কোম্পানির জন্য অ্যাডমিন সেট আপ করার সময় একটি সমস্যা তৈরি করে, কারণ এতে চুক্তি এবং অবস্থান উভয়ের জন্য ইনলাইন রয়েছে এবং আপনি বর্তমানে সম্পাদনা করছেন এমন কোম্পানির অনুসারে অবস্থানের জন্য চুক্তির এম 2 মি বিকল্পগুলি সঠিকভাবে ফিল্টার করা হয় না।

সংক্ষেপে, এর মতো কিছু করার জন্য আমার কিছু অ্যাডমিন বিকল্প প্রয়োজন:

# 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')

ফিল্টারিং প্রক্রিয়া বেস সংস্থাএডমিনে স্থাপন করা হয়েছিল, বা এটি চুক্তিবদ্ধরেখায় স্থাপন করা হয়েছে কিনা তা শেষ পর্যন্ত আমি পাত্তা দেব না। (এটি ইনলাইনে স্থাপন করা আরও অর্থবোধ করে, তবে বেস চুক্তিটিকে 'স্ব' হিসাবে উল্লেখ করা শক্ত করে তোলে))

এখানে কি এমন কেউ আছেন যিনি এই খারাপভাবে প্রয়োজন শর্টকাটের মতো সোজা কিছু জানেন? আমি যখন এই ধরণের জিনিসটির জন্য পিএইচপি অ্যাডমিনগুলি তৈরি করেছি তখন এটি প্রাথমিক কার্যকারিতা হিসাবে বিবেচিত হয়েছিল! আসলে, এটি সর্বদা স্বয়ংক্রিয় ছিল এবং আপনি যদি সত্যিই এটি না চান তবে অক্ষম করতে হয়েছিল!


0

আরও সাধারণ উপায়ে অ্যাডমিন ক্লাসে get_for কল করা। এটি নন-ডাটাবেস ক্ষেত্রেও কাজ করে। উদাহরণস্বরূপ এখানে আমার ফর্মটিতে '_terminal_list' নামে একটি ক্ষেত্র রয়েছে যা get_list (অনুরোধ) থেকে বেশ কয়েকটি টার্মিনাল আইটেম বেছে নেওয়ার জন্য বিশেষ ক্ষেত্রে ব্যবহার করা যেতে পারে, তারপরে অনুরোধের ভিত্তিতে ফিল্টারিং:

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
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.