سوالات متداول پایتون/جنگو

با سلام و درود، داخل این پست که به مرور هر دفعه بروز میشه، میخوام به پاسخ سوالات متداول برنامه نویسی پایتون و فریمورک جنگو بپردازم، اگر هم خودتون سوالی داشتید داخل کامنت های هیمن پست مطرح کنید تا پاسخش رو داخل همین پست به اشتراک بزاریم 🙂

اکثر سوالات ساده هستند و پاسخ های کوتاهی دارند و در کل امیدوارم حاصل این نوشته یه مجموعه سوال خوب و کاربردی باشه.

توجه توجه: اگر بهنگام باز کردن هر آکوردئون، باکس مربوط به کد ها بسته بود و چیزی نشون نمیداد، کافیه به صورت زیر عمل کنید:

sort کردن نتایج سریالایزر با توجه فیلد child آن در جنگو

خب هدف از مشخص هست، با یه مثال آموزش رو پیش می ریم، فرض کنید دو تا سریالایزر داریم و میخواهیم نتایج سریالایزر رو با توجه به یه فیلد بچه آن مرتب کنیم، دو تا مدل داریم به نام های subject و group که را بطه بینشون والد و فرزندی هست، بعنی هر subject میتونه چند تا groupداشته باشه، حالا قصدمون اینه زمانی که با سریالایزر تمامی subject ها همراه با فرزندانشون ( group) ها رو نشون دادیم، group ها نیز sortشده باشند (با توجه به یک فیلد از خود مدل group):

class NewAccessLevelGroupNestedSerializer(ModelSerializer):
    accecclevelgroup_subgroup =  NewAccessLevelSubGroupNestedSerializer(many = True)
    accecclevelgroup_subnav =  NewAccessLevelSubNavNestedSerializer(many = True)
    accecclevelaction_group = NewAccessLevelActionNestedSerializer(many = True)
    class Meta:
        model = AccessLevelGroup
        fields = '__all__' 

class NewAccessLevelSubjectNestedSerializer(ModelSerializer):
    accesslevel_groups = NewAccessLevelGroupNestedSerializer(many = True)  
    accecclevelaction_subject = NewAccessLevelActionNestedSerializer(many = True) 
    class Meta:
        model = AccessLevelSubject
        fields = '__all__'

میخوایم نتایج سریالایزری که nested شده زمانی که نمایش داده میشه با توجه به فیلد مورد نظر خودمون مرتب باشه، تو مثال فوق میخواهیم فقط سریالایزر پایینی فراخوانی شد و تو نتایجش نتایج سریالایزر بالا رو هم نشون داد، نتایج سریالایزر بالایی (تودرتو شده – nested) با توجه به یکی از فیلد های مدل سریالایزر بالایی مرتب باشه.

کافیه متد زیر را به سریالایزر اضافه کنیم:

class NewAccessLevelSubjectNestedSerializer(ModelSerializer):
    accesslevel_groups = NewAccessLevelGroupNestedSerializer(many = True)  
    accecclevelaction_subject = NewAccessLevelActionNestedSerializer(many = True) 
    class Meta:
        model = AccessLevelSubject
        fields = '__all__'
    # sort in serializer by sadegh
    def to_representation(self, instance):
        response = super().to_representation(instance)
        response["accesslevel_groups"] = sorted(response["accesslevel_groups"], key=lambda x: x["index"])
        return response

رابطه فرزند و والد روی مدل های جنگو

برای داشتن رابطه فرزند و پدری (child , parent) روی یک مدل در پایتون/جنگو، کافیست فیلد مورد نظر داخل مدل را به صورت زیر تعیریف کنید:

parent = models.ForeignKey('self', blank=True, null=True, related_name='children') 

همچنین برای دسترسی به فرزندان یا پدر از دو خط کد زیر استفاده کنید:

a2.parent = a1
a1.children.all()

چگونه در پایتون تاریخ را با string جمع کنیم (متصل کنیم)

نمیشه به راحتی date را با یه string متصل کرد. برای مثال اگر کد زیر را داشته باشیم:

s = "sadegh " + datetime.now()

با ارور زیر مواجه خواهید شد:

TypeError: cannot concatenate ‘str’ and ‘datetime.time’ objects

برای رفع مشکل کافیه تاریخ را با تابع strftime به متن تبدیل کنیم، به تغییر یافته مثال نخست توجه کنید:

s = "sadegh " + datetime.now().strftime("%Y-%m-%d_%H:%M:%S")

حذف یک رکورد از مدل داخل جنگو

SomeModel.objects.filter(id=id).delete()

or

instance = SomeModel.objects.get(id=id)

instance.delete()

بررسی پر یا خالی بودن رابطه چند به چند (many to many) در پایتون

parent_parts =parent.parts
parent_parts.exists():

تغییر نام فیلد در سریالایزر جنگو رست فریمورک – DRF

برای این منظور به مثال زیر توجه کنید. یک مدل داریم به نام sadegh که میخواهیم فیلد name در سریالایزر با نام ename نمایش داده شود.

class Sadegh(models.Model):
    name = models.CharField(max_length=256)
    persian_name = models.CharField(max_length=256, blank=True)
    objects = models.GeoManager()
    
    def __unicode__(self):
        return '%s' % self.name

حال سریالایزر جهت تغییر نام فیلد name به ename به صورت زیر می باشد:

class SadeghSerializer(serializers.ModelSerializer):
    ename = serializers.SerializerMethodField('get_alternate_name')

    class Meta:
        model = Park
        fields = '__all__'

    def get_alternate_name(self, obj):
        return obj.name

همچنین می توانید از serializers.CharField همراه با sourceاستفاده کنید، به شکل زیر :

class SadeghSerializer(serializers.ModelSerializer):
    ename = serializers.CharField(source='name')
    class Meta:
        model = Park
        fields = '__all__'

فیلتر کردن فیلد کلید خارجی برای نمایش در جنگو ادمین

با یه مثال جلو میریم که فهم مسئله و حل آن راحتتر باشه، فرض کندی مدلی داریم به نام access که دو تا فیلد داره با نام های sub_1 و sub_2 که هر دو کلید خارجی هستن روی یک مدل به نام sub ، حال به صورت پیش فرض داخل جنگو ادمین برای انتخاب sub_1 و sub_2 لیستی از تمامی ردیف های مدل sub را نشان می دهد که انتخاب کنید. حال ما می خواهیم برای sub1 فقط ردیف هایی از subو نشون بده که شرط مورد نظر ما رو داشته باشه و برای sub_2 نیز ردیف هایی با شرط دیگه. پس ابتدا مدل های مثال مورد نظر را ببینید:

class Access(TimeStampedModel):
    name = models.CharField(
        blank=True,
        null=True,
        max_length=200,
    )
    sub_nav_groups = models.ManyToManyField(
        'Sub',
        blank=True, 
        related_name='Access_sub_nav_groups'
    )
    sub_nav_subgroups = models.ManyToManyField(
        'Sub',
        blank=True, 
        related_name= 'Access_sub_nav_subgroups'
    )
    
class Sub(TimeStampedModel):
    """گروه ها همان منوهایی هستند که در هر موضوع وجود دارند"""
    access_group = models.ForeignKey(
        AccessGroup,
        on_delete=models.SET_NULL,
        blank=True,
        null=True,
        related_name='acceccgroup_subnav'
    )
    access_sub_group = models.ForeignKey(
        AccessSubGroup,
        on_delete=models.SET_NULL,
        blank=True,
        null=True,
        related_name='acceccsubgroup_subnav'
    )

برای فیلتر کردن خروجی های قابل انتخاب در جنگو ادمین کافیست از متد render_change_form داخل کلاس مورد نظر برای مدل مان داخل ادمین استفاده کنیم، برای مثال فوق به شکل زیر باید انجام داد:

class AccessAdmin(admin.ModelAdmin):
    list_display = [....]
    list_filter = [....]
    def render_change_form(self, request, context, *args, **kwargs):
         context['adminform'].form.fields['sub_nav_groups'].queryset = Sub.objects.filter(access_group__isnull=False)
         context['adminform'].form.fields['sub_nav_subgroups'].queryset = Sub.objects.filter(access_sub_group__isnull=False)
         return super(CommonAccessLevelAdmin, self).render_change_form(request, context, *args, **kwargs)

روش بررسی پر یا خالی بودن دیتا فریم در پانداس

برای بررسی خالی بودن دیتافریم کافیه از متد emptyروی دیتا فریم مورد نظرتون استفاده کنید، برای مثال:

if df.empty:
    print('DataFrame is empty!')

استفاده از permission_classes فقط برای یک یا چند متد در Django rest Framework

فرض یک permission hریم که فقط می خواهیم برای متد های POST و DELETE اعمال شود، برای DRFباید چه کنیم؟ کافیست مثل زیر پیش بریم:

class TestManagementView(APIView):
    def get_permissions(self):
        if self.request.method in ["POST","DELETE"]:
            self.permission_classes = [TestPermission]
        return super(CommonAccessLevelGroupManagementView, self).get_permissions()
    
    def get(self,request):
      pass
    def post(self,request):
      pass

ارسال آرگومان های اضافه برای سریالایزر در جنگو

خب میخوایم یک یا چند تا آرگومان اضافی برای سریالایزر داخل جنگو بفرستیم. برای انکار کافیه از آرگومان context از serializer استفاده کنید.

برای مثال به تکه کد زیر توجه کنید:

داخل view داریم:

my_objects = MyModelSerializer(
    input_collection, 
    many=True, 
    context={'office_id': request.user.office.id}
).data

داخل سریالایزر (serializer) داریم:

class MyModelSerializer(serializers.ModelSerializer):
...

    is_office_object = serializers.SerializerMethodField('_is_office')
...

    def _is_office(self, obj):
        office_id = self.context.get("office_id")
        if office_id:
            return office_id in obj.my_objects.values_list("office_id", flat=True)
        return False
...

شماره objectهای ManyToManyField در جنگو

به مثال زیر توجه کنید:

ما یک سری پست داریم که هر پست نیز تعدادی لایک داره، حالا میخوایم تعداد لایک های هر پست رو بدست بیاریم ، مدل پست رو زیر مشاهده می کنید:

class Posts(models.Model):
    user_profile = models.ForeignKey(Profile, on_delete=models.CASCADE)
    likes = models.ManyToManyField(User, blank=True, related_name='likes')

حالا میخوایم تعداد لایک های یک پست مشخص (پستی که کاربر آن کاربر شماره ۲ است) رو بدست بیاریم:

from django.db.models import Count, Sum

likes = (
        Posts.objects
        .filter(user_profile=2)  # filtering the post of specific user
        .annotate(likes_count=Count('likes'))  # counting likes on each post
        .aggregate(total_likes=Sum('likes_count'))  # Summing likes on each post to give total likes
    )