با سلام و درود، داخل این پست که به مرور هر دفعه بروز میشه، میخوام به پاسخ سوالات متداول برنامه نویسی پایتون و فریمورک جنگو بپردازم، اگر هم خودتون سوالی داشتید داخل کامنت های هیمن پست مطرح کنید تا پاسخش رو داخل همین پست به اشتراک بزاریم 🙂
اکثر سوالات ساده هستند و پاسخ های کوتاهی دارند و در کل امیدوارم حاصل این نوشته یه مجموعه سوال خوب و کاربردی باشه.
توجه توجه: اگر بهنگام باز کردن هر آکوردئون، باکس مربوط به کد ها بسته بود و چیزی نشون نمیداد، کافیه به صورت زیر عمل کنید:
خب هدف از مشخص هست، با یه مثال آموزش رو پیش می ریم، فرض کنید دو تا سریالایزر داریم و میخواهیم نتایج سریالایزر رو با توجه به یه فیلد بچه آن مرتب کنیم، دو تا مدل داریم به نام های 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()
نمیشه به راحتی 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()
parent_parts =parent.parts parent_parts.exists():
برای این منظور به مثال زیر توجه کنید. یک مدل داریم به نام 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 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 ...
به مثال زیر توجه کنید:
ما یک سری پست داریم که هر پست نیز تعدادی لایک داره، حالا میخوایم تعداد لایک های هر پست رو بدست بیاریم ، مدل پست رو زیر مشاهده می کنید:
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 )
ارسال پاسخ