replacing field in queryset before return - django

I have the following view -
class DeployFilterView(generics.ListAPIView):
serializer_class = DefinitionSerializer
def get_queryset(self):
jobname = self.request.GET.get('jobname')
if jobname.count("\\") == 1:
jobname = jobname.replace("\\", "")
queryset = Jobmst.objects.db_manager('Admiral').filter(jobmst_name=jobname).exclude(jobmst_prntname__isnull=False, jobmst_dirty='X')
else:
parent, job = jobname.rsplit('\\', 1)
queryset = Jobmst.objects.db_manager('Admiral').filter(jobmst_prntname=parent, jobmst_name=job).exclude(jobmst_dirty='X')
return queryset
In that view there's a value field called "jobmst_runbook" which has a character that doesn't translate with the DRF XML renderer. What I'd like to be able to do is scan the queryset for a particular character - SOH or \u0001
If it finds this character I want to remove it before doing the return queryset

I solved this by doing the logic in my serializer. The serializer is now looking for the object that's causing the failure and stripping out the character.
class DefinitionSerializer(serializers.ModelSerializer):
runbook_url = serializers.SerializerMethodField('get_url')
# dependencies = serializers.RelatedField(many=True)
jobdep = serializers.HyperlinkedRelatedField(
source='jobdep_set', # this is the model class name (and add set, this is how you call the reverse relation of bar)
view_name='jobdep-detail' # the name of the URL, required
)
# triggers = serializers.RelatedField(many=True)
trgmst = serializers.HyperlinkedRelatedField(
source='trgmst_set', # this is the model class name (and add set, this is how you call the reverse relation of bar)
view_name='trgmst-detail' # the name of the URL, required
)
class Meta:
model = Jobmst
resource_name = 'jobmst'
depth = 2
fields = ('jobmst_id', 'jobmst_type', 'jobmst_prntid', 'jobmst_active', 'evntmst_id',
'jobmst_evntoffset', 'jobmst_name', 'jobmst_mode', 'jobmst_owner', 'jobmst_desc',
'jobmst_crttm', 'jobdtl_id', 'jobmst_lstchgtm', 'runbook_url', 'jobcls_id', 'jobmst_prntname',
'jobmst_alias', 'jobmst_dirty', 'job_dependencies', 'job_events')
def get_url(self, obj):
if obj.jobmst_runbook == None:
pass
else:
return force_text(obj.jobmst_runbook[:-5])

Related

graphene-django: 'Meta.model' without either 'Meta.fields' or 'Meta.exclude' has been deprecated since 0.15.0 and is now disallowed

I'm trying to return filtered results using django-graphene but it gives an error about error-message
class PatientType(DjangoObjectType):
class Meta:
model = Patients
exclude = ('active',)
interfaces = (relay.Node,)
class PatientsQuery(ObjectType):
get_patient = graphene.Field(PatientType, id=graphene.Int())
all_patients = graphene.List(
PatientType, first=graphene.Int(), skip=graphene.Int(), phone_no=graphene.Int()
)
upcoming_appointments = DjangoFilterConnectionField(PatientType)
#permissions_checker([IsAuthenticated, CheckIsOrganizationActive])
def resolve_upcoming_appointments(self, info, **kwargs) -> List:
d = datetime.today() - timedelta(hours=1)
settings.TIME_ZONE # 'Asia/Karachi'
aware_datetime = make_aware(d)
res = Patients.objects.filter(appointments__booking_date__gte=aware_datetime,
appointments__booking_date__day=aware_datetime.day,
appointments__status=True)
if res:
return res
return []
class Query(
organization_schema.OrganizationQuery,
inventory_schema.MedicineQuery,
patient_schema.PatientsQuery,
graphene.ObjectType,
):
pass
Specify the filter_fields attribute in the PatientType.Meta as
class PatientType(DjangoObjectType):
class Meta:
model = Patients
exclude = ('active',)
interfaces = (relay.Node,)
filter_fields = ["field_1", "field_2"]
Alternatively, you can either set the filter_fields=[] or filterset_class attribute in the Meta section
More examples can be found in the doc, GraphenePython- Filtering

Overwrite fields in Django Serializer

I am new in Django and I would like to overwrite the field value in create and update method of serializer. Here is my model=>
class Holiday(models.Model):
HolidayDay = models.DateField()
Created_DT = models.DateTimeField()
Created_Usr = models.CharField(max_length=20)
LastModified_Usr = models.CharField(max_length=20,blank=True)
LastModified_DT = models.DateTimeField(blank=True,null=True)
def __str__(self):
return str(self.HolidayDay)
Here is my serializer=>
class HolidaySerializer(serializers.ModelSerializer):
class Meta:
model=Holiday
fields = [
'id',
'HolidayDay',
'Created_DT',
'Created_Usr',
'LastModified_Usr',
'LastModified_DT'
]
def create(self,validated_data):
validated_data['Created_Usr'] ="Testing"
return Holiday.objects.create(**validated_data)
I would like to update Create_usr field value in create method and LastModified_usr field in update method. But why I can't overwrite the create_usr field as "Testing"?
Here is my views=>
def post(self,request):
holiday = request.data.get('holiday')
serializer = HolidaySerializer(data=holiday)
serializer.is_valid()
print(serializer.errors)
if serializer.is_valid():
holiday_saved=serializer.save()
return Response({"success":"Holiday '{}' created successfully".format(holiday_saved.HolidayDay)})
def put(self,request,pk):
save_holiday = get_object_or_404(Holiday.objects.all(),pk=pk)
data = request.data.get('holiday')
serializer = HolidaySerializer(instance=save_holiday,data=data,partial=True)
if serializer.is_valid(raise_exception = True):
holiday_saved=serializer.save()
return Response({"sucess": "Holiday '{}' updated successfully".format(holiday_saved.HolidayDay)})
Your create method is not defined in your Serializer class, instead it is part of your Meta class. You should be able to solve it by moving your create method to your HolidaySerializer:
class HolidaySerializer(serializers.ModelSerializer):
def create(self,validated_data):
validated_data['Created_Usr'] = "Testing"
return Holiday.objects.create(**validated_data)
class Meta:
model=Holiday
fields = [
'id',
'HolidayDay',
'Created_DT',
'Created_Usr',
'LastModified_Usr',
'LastModified_DT'
]

Custom FilterSet doesn't filter by two fields at the same time

I wrote custom FilterSet to filter queryset by two fields but it doesn't work properly when it's filtering on two fields at the same time.
my FilterSet:
class EventFilter(filters.FilterSet):
values = None
default = None
category = filters.ModelMultipleChoiceFilter(
queryset=EventCategory.objects.all(),
)
interval = filters.CharFilter(
method='filter_interval'
)
class Meta:
model = Event
fields = ('category', 'interval')
def filter_interval(self, queryset, name, value):
if self.request.query_params.get('current_time'):
try:
interval = getattr(self, f'get_{value}_interval')()
interval = list(map(lambda date: self.to_utc(date), interval))
return self.queryset.filter(Q(status=Event.STARTED) | (Q(status=Event.NOT_STARTED, start_at__range=interval)))
except Exception as e:
pass
return queryset
APIView:
class ListEventsAPIView(generics.ListAPIView):
serializer_class = ListEventsSerializer
filter_class = EventFilter
search_fields = 'title',
filter_backends = filters.SearchFilter, DjangoFilterBackend
def get_queryset(self):
return Event.objects.filter(Q(status=Event.STARTED) | (Q(status=Event.NOT_STARTED) & Q(start_at__gte=date)))
Here is generated SQL when I'm trying to filter only by category:
SELECT "*" FROM "events" WHERE (("events"."status" = 'started'
OR ("events"."status" = 'not_started'
AND "events"."start_at" >= '2019-06-19T13:24:26.444183+00:00'::timestamptz))
AND "events"."category_id" = 'JNPIZF54n5q')
When I'm filtering on both:
SELECT "*" FROM "events" WHERE (("events"."status" = 'started'
OR ("events"."status" = 'not_started' AND "events"."start_at" >= '2019-06-19T13:24:26.444183+00:00'::timestamptz))
AND ("events"."status" = 'started' OR ("events"."start_at" BETWEEN '2019-06-19T07:16:48.549000+00:00'::timestamptz AND '2019-06-30T20:59:59.000059+00:00'::timestamptz AND "events"."status" = 'not_started')))
Your issue is in this line:
return self.queryset.filter(Q(status=Event.STARTED) | (Q(status=Event.NOT_STARTED, start_at__range=interval)))
You're using queryset from FilterSet class itself. This queryset doesn't have any previous filters applied, so by using it you're cancelling another filter. Just remove self. from this line to use queryset that is passed to this function as a parameter and everything will work fine.

Override page_size & ordering of CursorPagination in Django Rest Framework

I using CursorPagination of Django Rest Framework, I want to override default page_size(10) and ordering('timestamp') in a single viewset. How can I do this?
I tried with my viewset but it's not success:
from rest_framework.pagination import CursorPagination
class ListAPIView(ListAPIView):
queryset = Cake.objects.all()
permission_classes = [AllowAny]
serializer_class = ListSerializer
pagination_class = CursorPagination
filter_backends = (OrderingFilter, DjangoFilterBackend)
filter_class = CakeListFilter
filterset_fields = ('cake_type', 'user__username')
ordering = '-date'
page_size = 5
You may create a new class inheriting from CursorPagination class in order to set custom page_size and/or max_page_size like so:
class CustomPageSizeCursorPagination(CursorPagination):
page_size = 5
max_page_size = 100
And then use this class as the pagination_class field of your viewset
WARNING: The following code is untested
Another option is to write a custom paginator class that gets the page size from the viewset. For example:
class PageSizeInViewSetCursorPagination(CursorPagination):
def get_page_size(self, request, viewset_page_size):
if self.page_size_query_param:
try:
return _positive_int(
request.query_params[self.page_size_query_param],
strict=True,
cutoff=self.max_page_size
)
except (KeyError, ValueError):
pass
return viewset_page_size or self.page_size
def paginate_queryset(self, queryset, request, view=None):
# Get the page_size from the viewset and then decide which page_size to use
viewset_page_size = getattr(view, 'page_size', None)
page_size = self.get_page_size(request, viewset_page_size)
# What follows is copy/paste of the code from CursorPagination paginate_queryset method
if not self.page_size:
return None
self.base_url = request.build_absolute_uri()
self.ordering = self.get_ordering(request, queryset, view)
self.cursor = self.decode_cursor(request)
if self.cursor is None:
(offset, reverse, current_position) = (0, False, None)
else:
(offset, reverse, current_position) = self.cursor
# Cursor pagination always enforces an ordering.
if reverse:
queryset = queryset.order_by(*_reverse_ordering(self.ordering))
else:
queryset = queryset.order_by(*self.ordering)
# If we have a cursor with a fixed position then filter by that.
if current_position is not None:
order = self.ordering[0]
is_reversed = order.startswith('-')
order_attr = order.lstrip('-')
# Test for: (cursor reversed) XOR (queryset reversed)
if self.cursor.reverse != is_reversed:
kwargs = {order_attr + '__lt': current_position}
else:
kwargs = {order_attr + '__gt': current_position}
queryset = queryset.filter(**kwargs)
# If we have an offset cursor then offset the entire page by that amount.
# We also always fetch an extra item in order to determine if there is a
# page following on from this one.
results = list(queryset[offset:offset + self.page_size + 1])
self.page = list(results[:self.page_size])
# Determine the position of the final item following the page.
if len(results) > len(self.page):
has_following_position = True
following_position = self._get_position_from_instance(results[-1], self.ordering)
else:
has_following_position = False
following_position = None
# If we have a reverse queryset, then the query ordering was in reverse
# so we need to reverse the items again before returning them to the user.
if reverse:
self.page = list(reversed(self.page))
if reverse:
# Determine next and previous positions for reverse cursors.
self.has_next = (current_position is not None) or (offset > 0)
self.has_previous = has_following_position
if self.has_next:
self.next_position = current_position
if self.has_previous:
self.previous_position = following_position
else:
# Determine next and previous positions for forward cursors.
self.has_next = has_following_position
self.has_previous = (current_position is not None) or (offset > 0)
if self.has_next:
self.next_position = following_position
if self.has_previous:
self.previous_position = current_position
# Display page controls in the browsable API if there is more
# than one page.
if (self.has_previous or self.has_next) and self.template is not None:
self.display_page_controls = True
return self.page
Note that in the above example the page_size from the request always takes precedence over whatever you have set up in your code. Then the viewset_page_size is the second in line and lastly the deafult page_size from the Pagination class.
Here is a custom pagination class that extends CursorPagination. It checks for ordering and page_size attributes defined in the viewset and if they exist use them. If not, fallback to original settings defined in the pagination class itself.
class NewsCursorPaginator(CursorPagination):
ordering = 'title'
page_size = 5
# get_page_size do not have view attribute, so we have our custom one
def get_custom_page_size(self, request, view):
viewset_page_size = getattr(view, 'page_size', None)
if viewset_page_size:
self.page_size = viewset_page_size
return super(NewsCursorPaginator, self).get_page_size(request)
def get_ordering(self, request, queryset, view):
viewset_ordering = getattr(view, 'ordering', None)
if viewset_ordering:
self.ordering = viewset_ordering
return super(NewsCursorPaginator, self).get_ordering(request, queryset, view)
def paginate_queryset(self, queryset, request, view=None):
self.page_size = self.get_custom_page_size(request, view)
return super(NewsCursorPaginator, self).paginate_queryset(queryset, request, view)
This implementation takes "limit" (page_size) as an optional querystring parameter.
class CursorPagination(pagination.CursorPagination):
page_size = settings.REST_FRAMEWORK["PAGE_SIZE"]
def get_custom_page_size(self, request, view):
try:
self.page_size = int(request.GET.get("limit"))
except (ValueError, TypeError):
pass
return super().get_page_size(request)
def paginate_queryset(self, queryset, request, view=None):
self.page_size = self.get_custom_page_size(request, view)
return super().paginate_queryset(queryset, request, view)

Serializing django-mptt trees in Tastypie

How do I serialize django-mptt trees in Tastypie?
I want to use django-mptt's cache_tree_children(). I've tried applying in different Tastypie hooks, but it throws an error.
Without the cache_tree_children method you could probably have your children serialized by simply hooking up a ToManyField with full=True pointing at the children property:
class MenuResource(ModelResource):
children = fields.ToManyField('self', 'children', null=True, full=True)
parent = fields.ToOneField('self', 'parent', null=True)
class Meta:
queryset = Menu.objects.all()
To implement the cache_tree_children function you could write your own ToManyField subclass that overrides the standard dehydrate function. Please note, that I only tested this solution very superficially:
def dehydrate(self, bundle):
if not bundle.obj or not bundle.obj.pk:
if not self.null:
raise ApiFieldError("The model '%r' does not have a primary key and can not be used in a ToMany context." % bundle.obj)
return []
the_m2ms = None
previous_obj = bundle.obj
attr = self.attribute
if isinstance(self.attribute, basestring):
attrs = self.attribute.split('__')
the_m2ms = bundle.obj
for attr in attrs:
previous_obj = the_m2ms
try:
the_m2ms = getattr(the_m2ms, attr, None)
except ObjectDoesNotExist:
the_m2ms = None
if not the_m2ms:
break
elif callable(self.attribute):
the_m2ms = self.attribute(bundle)
if not the_m2ms:
if not self.null:
raise ApiFieldError("The model '%r' has an empty attribute '%s' and doesn't allow a null value." % (previous_obj, attr))
return []
self.m2m_resources = []
m2m_dehydrated = []
# There goes your ``cache_tree_children``
for m2m in cache_tree_children(the_m2ms.all()):
m2m_resource = self.get_related_resource(m2m)
m2m_bundle = Bundle(obj=m2m, request=bundle.request)
self.m2m_resources.append(m2m_resource)
m2m_dehydrated.append(self.dehydrate_related(m2m_bundle, m2m_resource))
return m2m_dehydrated
One of the main advantages of this method is that you don't have to care about detail / list view constraints / differences no more. You even can parameterize this aspect of your resource further down until you got some kind of default behavior that fits your needs. Field-based, that is. Which I think is cool.
This is how I solved it:
class MenuResource(ModelResource):
parent = fields.ForeignKey('self', 'parent', null=True)
class Meta:
serializer = PrettyJSONSerializer()
queryset = Menu.objects.all().select_related('parent')
include_resource_uri = False
fields = ['name']
def get_child_data(self, obj):
data = {
'id': obj.id,
'name': obj.name,
}
if not obj.is_leaf_node():
data['children'] = [self.get_child_data(child) \
for child in obj.get_children()]
return data
def get_list(self, request, **kwargs):
base_bundle = self.build_bundle(request=request)
objects = self.obj_get_list(bundle=base_bundle,
**self.remove_api_resource_names(kwargs))
sorted_objects = self.apply_sorting(objects, options=request.GET)
paginator = self._meta.paginator_class(
request.GET, sorted_objects,
resource_uri=self.get_resource_uri(), limit=self._meta.limit,
max_limit=self._meta.max_limit,
collection_name=self._meta.collection_name
)
to_be_serialized = paginator.page()
from mptt.templatetags.mptt_tags import cache_tree_children
objects = cache_tree_children(objects)
bundles = []
for obj in objects:
data = self.get_child_data(obj)
bundle = self.build_bundle(data=data, obj=obj, request=request)
bundles.append(self.full_dehydrate(bundle))
to_be_serialized[self._meta.collection_name] = bundles
to_be_serialized = self.alter_list_data_to_serialize(request,
to_be_serialized)
return self.create_response(request, to_be_serialized)
If you're not using pagination you can just take that part out. That's what I did.