I'm trying to add a dynamic queryset to a model-form/formset field from a url kwarg <int:url_kwarg>, no luck so far - django-forms

Edit: It appears that the problem I'm experiencing is directly related to the formset_factory() call. For some reason or other, I have been unable to access the kwargs from the view if I pass the form through the function.
I'm building a web app that utilizes high normalization in the data-structure. There are many many-to-many and one-to-many relationships to prevent excess null records and database-bloat. I want to add more entries to a model while excluding existing entries from the model.choice field.
my code looks like this:
the form:
class ExtraAddForm(forms.ModelForm):
def __init__(self, url_kwarg, *args, **kwargs):
super(ExtraAddForm, self).__init__(self, *args, **kwargs)
list_to_exclude = []
query_target = models.Model.objects.get(fk_id=url_kwarg)
for object in query_target:
list_to_exclude.append(object.fk_id.id)
new_queryset = models.Model.objects.exclude(fk_id__in=list_to_exclude)
self.fields['fk_id'].queryset= new_queryset
class Meta:
model = models.Model
fields= ['fk_id','field_b'}
the view:
class AddOjbectsView(FormView):
formset = formset_factory(ExtraAddForm(url_kwarg), can_delete=True)
model = models.Model
url_kwarg = 'url_kwarg'
form_class = formset
template_name = 'some-template.html'
extra_context = {'some_object': models.Model2.objects.all,
'model_object': models.Model.objects.all,
'formset': formset,
'view_type_create': True
}
def __init__(self, *args, **kwargs):
kwargs['url_kwarg']= self.kwargs.get(self.url_kwarg)
super().__init__(self,*args,**kwargs)
def get(self, request, *args, **kwargs):
request.session['url_kwarg'] = self.kwargs.get(self.url_kwarg)
return super().get(self, request, *args, **kwargs)
def post(self, request, *args, **kwargs):
#this works so I'm not re-typing it
def get_context_data(self, **kwargs):
"""Insert the form into the context dict."""
if 'url_kwarg' not in kwargs:
kwargs['url_kwarg'] = self.kwargs.get(self.url_kwarg)
return super().get_context_data(**kwargs)
#this works, but only in the get_context. Its not working as a solution to my problem.
def get_success_url(self):
#this works, not re-typing
My template has Javascript to handle multiple formsets, and I've tested it with a non-dynamic queryset. The only piece I'm having trouble with is taking the keyword argument from the URL and passing it to the form at init.

Have you tried using FormView.get_form_kwargs method?
Its described in docs
class AddOjbectsView(FormView):
def get_form_kwargs(self):
form_kwargs = super().get_form_kwargs()
form_kwargs['url_kwarg'] = self.kwargs['url_kwarg']
return form_kwargs

It would seem that the answer to my problem is to deprecate the process in favor of session variables at form validation, making use of the clean data functions. My methods above were generated from ignorance of the order of django operations, and shouldn't preoccupy anyone any further.

Related

django - pass request.user to ModelForm

i am having a tricky issue. In my views.py i am passing a form in a DetailView. But i am not using a FormMixin. That has the reason that i only want my template to render the form. In the template i use that form to trigger an UpdateView.
class UpdateDetailView(LoginRequiredMixin, DetailView):
model = Balance
def get_context_data(self, **kwargs):
context = super(UpdateDetailView, self).get_context_data(**kwargs)
context['form'] = ManagerForm
return context
def get_queryset(self, *args, **kwargs):
request = self.request
pk = self.kwargs.get('pk')
select = self.kwargs.get('select')
queryset = Balance.objects.filter(pk=pk).filter(owner = request.user).filter(select = select)
return queryset
class BalanceUpdateView(LoginRequiredMixin, UpdateView):
form_class = ManagerForm
model = Balance
def get_success_url(self):
return reverse('url-year', kwargs={'year': self.object.year, 'select': self.object.select})
So far, so good. The issue is that the form in the UpdateDetailView the ChoiceFields are showing select option of other users which one is not supposed to see. Thus i need to override the queryset of the Modelform. Usually i could use the get_form_kwargs method to pass the request.user to the form. But since the UpdateDetailView is not a FormView it doesnt have that (i know, coz i tried desperately). I also tried context['form'] = ManagerForm(initial = {'user': self.request.user}) in the get_context_data method. But i was unable to access user properly with the __init__ method in the forms.py. I got a key error. If i passed it as arg then i get an attribute error. Does anyone have a solution to that problem? Do i need to use a FormMixin?
Thank you for your replies!

How to avoid code duplication in DRF views

class AddUsersView(UpdateAPIView):
permission_classes = [IsAuthenticated]
def patch(self, request: Request, *args, **kwargs):
if ...
return successful_response({'data': "users added"})
else:
raise MeetingRoomDoesNotExist
class SubtractUsersView(UpdateAPIView):
permission_classes = [IsAuthenticated]
def patch(self, request: Request, *args, **kwargs):
if ...
return successful_response({'data': "users subtracted"})
else:
raise MeetingRoomDoesNotExist
It seems my code is not DRY.What can I use to avoid code duplication.
DRF views are not different from other python classes. Your two best options here are basically to :
Integrate the duplicated code in a function you'd call in both patch functions instead of rewriting it
Override the patch() function in a class and make your views inherit from it

DRF - How to get created object in CreateAPIView

My goal very much resembles to what has been asked in this question but from the perspective of DRF, rather than forms.
So basically the question is, how can I get the newly created object in the following code snippet:
TestSerializer(serializers.ModelSerializer)
class Meta:
fields = '__all__'
model = TestModel
class TestView(generics.CreateAPIView):
serializer_class = TestSerializer
def create(self, request, *args, **kwargs):
response = super(TestView, self).create(request, *args, **kwargs)
created_model_instance = .... ?
print(created_model_instance.id)
return response
You can override perform_create and use serializer.save to get the created object, like:
class TestView(generics.CreateAPIView):
serializer_class = TestSerializer
def perform_create(self, serializer):
"""Some doc here!"""
obj = serializer.save()
print(obj.id)

Django-rest-pandas TemplateDoesNotExist

I have created a PandasSimpleView which overrides the 'get_data' method to return a Pandas Dataframe.
I have put the following renderer_classes inside the PandasSimpleView method :
renderer_classes = (PandasHTMLRenderer, PandasCSVRenderer, PandasJSONRenderer, PandasTextRenderer,)
I am able to render CSV, text and JSON successfully except HTML. My assumption is i dont need a template to be able to render the HTML if i am using PandasSimpleView since Django-REST-pandas framework do not require template but views and url.
So, why am i still receiving an exception TemplateDoesNotExist?
enter image description here
class QuestionStats (PandasSimpleView):
renderer_classes = (PandasHTMLRenderer, PandasCSVRenderer, PandasJSONRenderer, PandasTextRenderer,)
def get_data(self, request, *args, **kwargs):
curr_slug = self.request.query_params.get('slug', None)
data = get_question_stats(curr_slug)
return data
def get(self, request, *args, **kwargs):
data = self.get_data(request, *args, **kwargs)
return Response(data, template_name='questions/questionstats.html')
The problem is resolved, by overriding the 'get' method and returning Response with the template name location.

DRF-Haystack SearchIndex returning from ALL index classes?

I'm using haystack with elasticsearch backend in django, and drf-haystack to implement serializers for results.
I first created a StudentIndex, which indexes over StudentProfiles to use in searches for students in search_indexes.py
class StudentIndex(indexes.SearchIndex, indexes.Indexable):
text = indexes.CharField(
document=True,
use_template=True,
template_name="search/indexes/user_search_text.txt")
//other indexes or indices or whatever
def get_model(self):
return StudentProfile
def index_queryset(self, using=None):
return StudentProfile.objects.filter(user__is_active=True)
Which is passed to the serializer and viewset in views.py:
class StudentSerializer(HaystackSerializer):
class Meta:
index_classes = [StudentIndex]
class StudentSearchView(ListModelMixin, HaystackGenericAPIView):
index_models = [StudentProfile]
serializer_class = StudentSerializer
pagination_class = None
def get(self, request, *args, **kwargs):
return self.list(request, *args, **kwargs)
and everything was fine and dandy....UNTIL I added two more indexes over other profiles and a single view/serializer to handle them. All exist in the same respective files:
search_indexes.py
class TeacherIndex(indexes.SearchIndex, indexes.Indexable):
text = indexes.CharField(
document=True,
use_template=True,
template_name="search/indexes/user_search_text.txt")
//other indexes or indices or whatever
def get_model(self):
return TeacherProfile
def index_queryset(self, using=None):
return TeacherProfile.objects.filter(user__is_active=True)
class StaffIndex(indexes.SearchIndex, indexes.Indexable):
text = indexes.CharField(
document=True,
use_template=True,
template_name="search/indexes/user_search_text.txt")
//other indexes or indices or whatever
def get_model(self):
return StaffProfile
def index_queryset(self, using=None):
return StaffProfile.objects.filter(user__is_active=True)
and added to views.py:
class StaffSerializer(HaystackSerializer):
class Meta:
index_classes = [StaffIndex, TeacherIndex]
class StaffSearchView(ListModelMixin, HaystackGenericAPIView):
index_models = [StaffProfile, TeacherProfile]
serializer_class = StaffSerializer
pagination_class = None
def get(self, request, *args, **kwargs):
return self.list(request, *args, **kwargs)
NOW....each view goes to its own url endpoint (/student-search and /staff-search respectively), but ONLY the staff-search endpoint properly returns Staff and TeacherProfile results, while the student-search, in a seperate endpoint with seperate view and models and indexes all explicitly stated, is return StudentProfiles AND Teacher and StaffProfiles and I can not for the life of me figure out why.
If anyone has run into this before, I'd really appreciate help solving, and more importantly, understanding, this issue.
Thanks in advance!
Well, for future people with the same issues, here's all i needed to do to solve it.
I'm using the DRF-Haystack genericviewapi, and on the view, I simply defined the filter_queryset to use the proper haystack connection (which ignored other indices). So for example:
class StudentSearchView(ListModelMixin, HaystackGenericAPIView):
index_models = [StudentProfile]
serializer_class = StudentSerializer
pagination_class = None
def get(self, request, *args, **kwargs):
return self.list(request, *args, **kwargs)
def filter_queryset(self, queryset):
queryset = super(HaystackGenericAPIView, self).filter_queryset(queryset)
return queryset.using('student')
Everything now works properly :)
I have the same problem, but when i use the queryset.using('dynamic'), it raises django.core.exceptions.ImproperlyConfigured: The key 'dynamic' isn't an available connection.
class DynamicSearchView(ListModelMixin, HaystackGenericAPIView):
serializer_class = serializers.DynamicSearchSerializer
filter_backends = [HaystackHighlightFilter]
#swagger_auto_schema(
manual_parameters=[text, type]
)
def get(self, request, *args, **kwargs):
return self.list(request, *args, **kwargs)
def filter_queryset(self, queryset):
return super(DynamicSearchView, self).filter_queryset(queryset).using('dynamic')

Resources