query string, changes required in urls.py and view - django

A basic django question, but stumping me.
I have an existing view for an endpoint.
http://localhost:8001/v1/subject - which returns all subjects from the subject model.
I want to serve a new endpoint...
http://localhost:8001/v1/subject?owner_ids=60,61,62
What would be the changes required in the "urls.py" definition and
'''
def query_set
'''
method in views/subject.py,
I have added the method but it's not working as intended.
Here's the urls.py definition, any changes required to accommodate a query string?
router.register(r'subjects', views.SubjectsViewSet)
Should I do something like this?
url(r'^v1/subjects/',
views.SubjectViewSet.as_view({'get': 'list'}))
Also, here's my views/subjects.py file...the logic in def get_queryset may work, but how do I wire the urls.py entry so that the querystring localhost:8001/v1/subjects?owner_ids=60,61,62 is served together with the regular localhost:8001/v1/subjects ?
class SubjectViewSet(Subject.get_viewset()):
pagination_class = SubjectPagination
def get_queryset(self, *args, **kwargs):
owner_id_list =
self.request.GET['owner_ids'].split(',')
owner_id_list_integer = []
for i in owner_id_list:
owner_id_list_integer.append(int(i))
queryset = Subject.objects.all()
if owner_id_list is None:
return None
else:
return queryset.filter(organization__in=owner_id_list)
SubjectUserRoleViewSet = Subject.get_by_user_role_viewset(
SubjectViewSet, GroupRoleMap, Role)

Please, try this way.
def get_queryset(self, *args, **kwargs):
splited = self.request.GET['owner_ids'].split(',')
filtered_nums = list(filter(None, splited))
try:
get_nums = list(map(int, filtered_nums))
except ValueError as exc:
return Subject.objects.none()
else:
return Subject.objects.filter(organization__in=get_nums)
I hope, I do not have a mistake.

Related

How Do I Override GET in Django Detailview?

I have a FORMVIEW that is redirecting to a DETAILVIEW and that is working perfectly. The issue I'm having is when I try to combine Pagination with DetailView. When I try to leverage the pagination, the GET is essentially redirecting me to the FORMVIEW. I get why it's doing this...I'm telling it to. What I'm trying to work out is how I can put in some logic in my overridden GET. I tried to do a self.GET_OBJECT.pk...to see if I could determine if I'm on the current page and not the FORMVIEW but that didn't work...
Here's my DetailView....
def get_context_data(self, **kwargs):
context = super(SuggestionByNameDetailView, self).get_context_data(**kwargs)
attachments = SuggestionFiles.objects.filter(suggestion=self.object.pk).all()
comment_form = SuggestionCommentForm()
response_form = SuggestionCommentReplyForm()
activities= self.get_related_activities()
context['suggestion_comments'] = activities
context['page_obj'] = activities
context['attachments'] = attachments
context['comment_form'] = comment_form
context['response_form'] = response_form
return context
def get_related_activities(self):
queryset = self.object.suggestion_comments.all()
paginator = Paginator(queryset,5) #paginate_by
page = self.request.GET.get('page')
activities = paginator.get_page(page)
return activities
def get_object(self, queryset=None):
return get_object_or_404(Suggestion, id=self.request.GET.get("dropdown"))
def get(self, request, *args, **kwargs):
dropdown=self.request.GET.get("dropdown")
if dropdown is not None:
if Suggestion.objects.filter(Q(id=self.request.GET.get("dropdown"))).distinct():
self.object = self.get_object()
context = self.get_context_data(object=self.object)
return self.render_to_response(context)
else:
raise Http404
else:
messages.add_message(self.request, messages.INFO, 'Suggestion is required.')
return HttpResponseRedirect(reverse('Suggestions:suggestion_by_name'))
As mentioned I did try to do something like...if DROPDOWN is NONE and pk = self.get_object().pk...to essentially try and determine if I can just bypass the DROPDOWN logic in GET...but the PK is always present...I also tried to do something like self.request.GET.get('pk') but that didn't work either.
When I click to do the pagination I get redirected back to the FORMVIEW. I'm trying to avoid that once I'm on the DETAILVIEW. Thanks in advance for any thoughts on what I can do to resolve this.
So....after a couple of hours....turns out....GET may be an issue...but here's a way around it.....The "canned" django code...for pagination looks something like this...
Next
In my case...I simply needed to get more specific with my URL...so it now looks more like....
Next
Case closed. This one anyway. :).

Django Rest Framework return empty list in results randomly, but count still correct

When I'm trying to use custom filter it's working all the time in local development, but when we deploy to Production (Docker Swarm) We found an issue that sometime the API response is return empty results randomly, but the count is correct. Below is the example results from the API
API Response
{
'count': 1,
'next': 'http://localhost:8000/api/somethings/?email=test%40example.com&limit=0&offset=0',
'previous': None,
'results': []
}
Right now we need to restart a uwsgi service (By restarting docker swarm for this service) and the problem is fixed for a moment and randomly happen again.
Here is our DRF view
class SomeView(ListCreateAPIView):
queryset = SomeModel.objects.all()
serializer_class = SomeModelSerializer
filter_backends = (OrderingFilter, DjangoFilterBackend)
filter_class = CustomFilter
ordering = ('id',)
def list(self, request, *args, **kwargs):
if request.GET.get('all', None):
# Do something
serializer = self.get_serializer(queryset, many=True)
return Response(serializer.data)
else:
return super(SomeView, self).list(self, request, *args, **kwargs)
Here is our CustomFilter
from django_filters.rest_framework.filters import CharFilter
import rest_framework_filters as filters
class CustomFilter(filters.FilterSet):
json_field_separator = '___'
json_field_is = CharFilter(name='json_field', method='filter_json_field')
json_field_is_not = CharFilter(name='json_field', method='exclude_json_field')
def split_lookup_field(self, value):
return dict(field.split(':') for field in value.split(self.json_field_separator))
def filter_json_field(self, queryset, name, value):
try:
lookup_field = self.split_lookup_field(value)
return queryset.filter(**lookup_field)
except (ValueError, FieldError):
return queryset.none()
def exclude_json_field(self, queryset, name, value):
try:
lookup_field = self.split_lookup_field(value)
except (ValueError, FieldError):
return queryset.none()
for query_arg, query_value in lookup_field.items():
queryset = queryset.exclude(**{query_arg: query_value})
return queryset
class Meta:
model = SomeModel
exclude = ['image', 'json_field']
Here is a version of Package we use for this project
Django==1.10.8
djangorestframework==3.6.4
django-filter==1.0.4
djangorestframework-filters==0.10.2
In GenericAPIView you can find a method which is called: get_queryset(), which looks like this:
def get_queryset(self):
"""
Get the list of items for this view.
This must be an iterable, and may be a queryset.
Defaults to using `self.queryset`.
This method should always be used rather than accessing `self.queryset`
directly, as `self.queryset` gets evaluated only once, and those results
are cached for all subsequent requests.
You may want to override this if you need to provide different
querysets depending on the incoming request.
(Eg. return a list of items that is specific to the user)
"""
assert self.queryset is not None, (
"'%s' should either include a `queryset` attribute, "
"or override the `get_queryset()` method."
% self.__class__.__name__
)
queryset = self.queryset
if isinstance(queryset, QuerySet):
# Ensure queryset is re-evaluated on each request.
queryset = queryset.all()
return queryset
It is a quick call, but I think your queryset is not re-evaluated on each request.
Take a look at this comment: # Ensure queryset is re-evaluated on each request.

Can't get dynamic python ModelChoiceField to work

I'm new to python and trying to understand how to get a dynamic ModelChoiceField to work. It works fine when I select an object with all but I'm trying to get the dropdown to reflect a user's attribute. Here is my code:
Forms.py
class ViewByMake(forms.Form):
dropdown = forms.ModelChoiceField(queryset=Make.objects.none())
def __init__(self, user, *args, **kwargs):
user = kwargs.pop('user')
super(ViewByMake, self).__init__(*args, **kwargs)
qs = Make.objects.filter(user=user)
self.fields['dropdown'].queryset = qs
self.fields['dropdown'].widget.attrs['class'] = 'choices1'
self.fields['dropdown'].empty_label = ''
Views.py
def view_bymake(request):
form = ViewByMake(request.POST or None, user=request.user)
if request.method == 'POST':
if form.is_valid():
make = form.cleaned_data['dropdown']
return HttpResponseRedirect(make.get_absolute_url1())
return render(request,'make/view_make.html',{'form':form})
This code works fine if I remove all user= references but then only returns the full make objects list which is not what I want. I found a very similar question on StackOverflow, but when I duplicated the code identically, it still doesn't work and it is giving me the following error:
init() got multiple values for argument 'user'
I searched the end of the internet on this topic. I'm open to other ideas if I'm approaching this poorly. I'm trying to basically get a filtered list based on criteria associated with a user's profile. I definitely need the drop down field to be specific to a user based on a profile setting. Thanks for your help in advance. I'm running django 1.11.2 and Python 3.6.1.
This is the updated model which need to include the user attribute which I didn't realize that I had to specify:
class Make(models.Model):
name = models.CharField(max_length=264,unique=True)
user = models.ForeignKey(User,null=True,on_delete=models.CASCADE)
Try with request, send request from form and get request in init method of form
views.py
def view_bymake(request):
form = ViewByMake(request.POST or None, request=request)
forms.py
def __init__(self, user, *args, **kwargs):
self.request = kwargs.pop('request', None)
super(ViewByMake, self).__init__(*args, **kwargs)
qs = Make.objects.filter(user=self.request.user)
self.fields['dropdown'].queryset = qs
self.fields['dropdown'].widget.attrs['class'] = 'choices1'
self.fields['dropdown'].empty_label = ''
The answer to my original question, how do I get user=user to work consists of making sure that your form, view, and model all reference user. I originally had the user reference in the view and the form correct, but I neglected to make sure user= was specified on the model I was referencing. I thought it was built in, but turns out you have to specifically reference it on your model. I'm new at this so it was a learning experience. On to the next challenge!

Django: Accessing parent object in new linline object

I have been combing through the internet for quite some while without finding any solution to this problem.
What I am trying to do...
I have the following models:
class TrackingEventType(models.Model):
required_previous_event = models.ForeignKey(TrackingEventType)
class TrackingEvent(models.Model):
tracking = models.ForeignKey(Tracking)
class Tracking(models.Model):
last_event = models.ForeignKey(TrackingEvent)
Now the main model is Tracking, so my admin for Tracking looks like this:
class TrackingEventInline(admin.TabularInline):
model = TrackingEvent
extra = 0
class TrackingAdmin(admin.ModelAdmin):
inlines = [TrackingEventInline]
That's it for the current setup.
Now my quest:
In the TrackingAdmin, when I add new TrackingEvent inlines, I want to limit the options of TrackingEventType to onlye those, that are allowed to follow on the last TrackingEvent of the Tracking. (Tracking.last_event == TrackingEventType.required_previous_event).
For this, I would need to be able to access the related Tracking on the InlineTrackingEvent, to access the last_event and filter the options for TrackingEventType accordingly.
So I found this: Accessing parent model instance from modelform of admin inline, but when I set up TrackingEventInline accordingly:
class MyFormSet(forms.BaseInlineFormSet):
def _construct_form(self, i, **kwargs):
kwargs['parent_object'] = self.instance
print self.instance
return super(MyFormSet, self)._construct_form(i, **kwargs)
class MyForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
print kwargs
self.parent_object = kwargs.pop('parent_object')
super(MyForm, self).__init__(*args, **kwargs)
class TrackingEventInline(admin.TabularInline):
form = MyForm
formset = MyFormSet
model = TrackingEvent
extra = 0
I get a KeyError at /admin/.../tracking/2/change/ 'parent_object' from self.parent_object = kwargs.pop('parent_object')
Does anyone know how to solve this? Am I approaching the problem the wrong way? I guess this would be pretty easy in a custom form in the frontend, but I really want to use the admin, because the whole application is built to be used from the admin, and it would be a hell lot of work to build a custom admin interface just because of this problem :)
Ok, so posting on StackOverflow is always helping to get the problem straight. I was able to put together a solution that works for me.
It includes defining my own Form in a outer function, as well as defining two InlineAdmin objects for TrackingEvent (one for update / edit, one just for insert).
Here's the code:
def create_trackingevent_form(tracking):
"""
"""
class TrackingEventForm(forms.ModelForm):
"""
Form for Tracking Event Inline
"""
def clean(self):
"""
May not be needed anymore, since event type choices are limited when creating new event.
"""
next_eventtype = self.cleaned_data['event_type']
tracking = self.cleaned_data['tracking']
# get last event, this also ensures last_event gets updated everytime the change form for TrackingEvent is loaded
last_eventtype = tracking.set_last_event()
if last_eventtype:
last_eventtype = last_eventtype.event_type
pk = self.instance.pk
insert = pk == None
# check if the event is updated or newly created
if insert:
if next_eventtype.required_previous_event == last_eventtype:
pass
else:
raise forms.ValidationError('"{}" requires "{}" as last event, "{}" found. Possible next events: {}'.format(
next_eventtype,
next_eventtype.required_previous_event,
last_eventtype,
'"%s" ' % ', '.join(map(str, [x.name for x in tracking.next_tracking_eventtype_options()]))
)
)
else:
pass
return self.cleaned_data
def __init__(self, *args, **kwargs):
# You can use the outer function's 'tracking' here
self.parent_object = tracking
super(TrackingEventForm, self).__init__(*args, **kwargs)
self.fields['event_type'].queryset = tracking.next_tracking_eventtype_options()
#self.fields['event_type'].limit_choices_to = tracking.next_tracking_eventtype_options()
return TrackingEventForm
class TrackingEventInline(admin.TabularInline):
#form = MyForm
#formset = MyFormSet
model = TrackingEvent
extra = 0
#readonly_fields = ['datetime', 'event_type', 'note']
def has_add_permission(self, request):
return False
class AddTrackingEventInline(admin.TabularInline):
model = TrackingEvent
extra = 0
def has_change_permission(self, request, obj=None):
return False
def queryset(self, request):
return super(AddTrackingEventInline, self).queryset(request).none()
def get_formset(self, request, obj=None, **kwargs):
if obj:
self.form = create_trackingevent_form(obj)
return super(AddTrackingEventInline, self).get_formset(request, obj, **kwargs)
I hope this helps other people with the same problem.. Some credit to the Stack Overflow threads that helped me come up with this:
Prepopulating inlines based on the parent model in the Django Admin
Limit foreign key choices in select in an inline form in admin
https://docs.djangoproject.com/en/1.9/ref/models/instances/#django.db.models.Model.clean_fields
Please do not hesitate to ask questions if you have any

Filter django query set by a non-database operation

Working with ReviewBoard 1.6.11, which uses Django 1.3.3.
There is a RepositoryManager class that has a method called 'accessible', defined as this:
class RepositoryManager(Manager):
def accessible(self, user, visible_only=True, local_site=None):
"""Returns repositories that are accessible by the given user."""
if user.is_superuser:
qs = self.all()
else:
q = Q(public=True)
if visible_only:
q = q & Q(visible=True)
if user.is_authenticated():
q = q | (Q(users__pk=user.pk) |
Q(review_groups__users=user.pk))
qs = self.filter(q).distinct()
return qs.filter(local_site=local_site)
The problem is that I want to filter the results of this query by something else that does not interact with the database (filesystem permissions of the user). accessible() needs to return a QuerySet, though. Otherwise, I would just create a list and populate it with the appropriate items from the result.
I'm fairly new to Django. Is this a reasonable thing to do, or am I going about this all wrong?
I was able to solve this by creating a QuerySet subclass like so:
class RepositoryQuerySet(QuerySet):
def __init__(self, *args, **kwargs):
super(RepositoryQuerySet, self).__init__(*args, **kwargs)
self._user_filter = None
def _clone(self, *args, **kwargs):
retval = super(RepositoryQuerySet, self)._clone(*args, **kwargs)
retval._user_filter = self._user_filter
return retval
def filter_by_user_access(self, user):
self._user_filter = user.username
def iterator(self):
for repo in super(RepositoryQuerySet, self).iterator():
if self._user_filter is None:
yield repo
continue
# ...logic for restricting access by user...
# If user has access, just yield repo. If not, just 'continue'
Then in RepositoryManager.accessible, right after the call to distinct(), call:
qs.filter_by_user_access(user)
Finally, for the manager to use the new query set class, create:
def get_query_set(self):
return RepositoryQuerySet(self.model, using=self.db)
in the RepositoryManager class.