I am building a view that I am passing in a uuid from the url. However when I try to access the kwarg, I get a "AttributeError: view object has no attribute 'kwargs'" error.
In my template, I am passing a UUID:
create/97261b96-23b8-4915-8da3-a90b7a0bdc8e/
The URL:
re_path(
r"^create/(?P<uuid>[-\w]+)/$",
views.DetailCreateView.as_view(),
name="detail_create"),
The View:
class DetailCreateView(SetHeadlineMixin, LoginRequiredMixin, InlineFormSetView):
inline_model = Detail
headline = "Create a Detail"
form_class = DetailForm
success_message = "Detail Added"
template_name = "details/detail_create.html"
def get_object(self, **kwargs):
return Post.objects.get_subclass(uuid=self.kwargs.get('uuid'))
def __init__(self, *args, **kwargs):
super(DetailCreateView, self).__init__(*args, **kwargs)
self.object = self.get_object()
self.model = self.object.__class__()
For context on what is happening -
Post is a model that is an InheritanceManager that other models (Product & Variation) inherit from.
Both models Product & Variation have a manytomanyfield to Detail.
Upon creating a Detail, I will be adding it to either the Product object or Variation object.
To set the model for the InlineFormSetView, I am trying to use the UUID to query for the object and dynamically set that based upon the class of the object I am trying to create a Detail for.
Question
Any ideas why I can't access the kwargs which is being sent in the URL path?
In as_view method kwargs and args attributes are assigned to the view after __init__ method. So when you call get_object inside __init__ it raises the error since self.kwargs is not assigned yet. To fix this error you can move
self.object = self.get_object()
self.model = self.object.__class__()
from __init__ to get_object:
class DetailCreateView(SetHeadlineMixin, LoginRequiredMixin, InlineFormSetView):
inline_model = Detail
headline = "Create a Detail"
form_class = DetailForm
extra = 10
success_message = "Detail Added"
template_name = "details/detail_create.html"
def get_object(self, **kwargs):
self.object = Post.objects.get_subclass(uuid=self.kwargs.get('uuid'))
self.model = self.object.__class__()
return self.object
def __init__(self, *args, **kwargs):
super(DetailCreateView, self).__init__(*args, **kwargs)
Try to use self.request.query_params.get('uuid')
Related
I want to switch between two models depending on which route I am in.
I am overwriting get_queryset() function to return the correct model:
class DynamicModelView(TemplateView, PageDescriptionListingMixin):
model = None
template_name = 'dynamic_model.html'
context_object_name = "accounts"
def get_context_data(self, **kwargs):
context = super(DynamicModelView, self).get_context_data(**kwargs)
self.add_page_text_to_context(context)
return context
def get_queryset(self):
if '/dynamic_user/' in self.request.path:
model = UserAccount
else:
model = AdminAccount
return model.objects.first()
As you can see in get_context_data I am injecting an object in context for AdminAccount but inside template I can't see it! in fact if I changed model from None to AdminAccount then it appears which I want that to happen dynamically.
Is there any way to switch models dynamically in Django?
override dispatch method.
def dispatch(self, request, *args, **kwargs):
if not self.model:
self.model = <MODEL>
return super(DynamicModelView, self).dispatch(request, *args, **kwargs)
I would like to pass a kwarg to set a modelform field but im struggling to figure out how to do it.
My URL is as follows:
url(r'^tent/create/(?P<munc>\d+)',views.TentCreate.as_view(),name='tent_create'),
My view is simply:
class TentCreate(CreateView):
model = Tent
form_class = TentForm
And my form:
class TentForm(ModelForm):
class Meta:
model = Tent
exclude =('asfo','niho')
def __init__(self, *args, **kwargs):
super(TentForm, self).__init__(*args, **kwargs)
self.fields['primary'].queryset = Mark.objects.filter(munc=self.kwargs['munc'])
from the model:
class Tent(models.Model):
primary = models.ForeignKey(Mark,on_delete=models.CASCADE)
I can render the form fine without overriding def __init, with no filtering applied to the 'primary' field.
However attempting to use the def __init code I've described above to pass the munc kwarg to the form field is resulting in the following error:
"'TentForm' object has no attribute 'kwargs'"
I've been going around in circles trying to work through this so I would be really appreciative if anyone is able to provide me some guidance to solve this. This is my first Django project so I'm learning how I go so I assume I have made some fundamental error somewhere here!
Try overriding get_form_kwargs method:
views.py
class TentCreate(CreateView):
model = Tent
form_class = TentForm
def get_form_kwargs(self):
kwargs = super(TentCreate, self).get_form_kwargs()
kwargs.update({'munc': self.kwargs['munc']})
return kwargs
forms.py
class TentForm(ModelForm):
class Meta:
model = Tent
exclude =('asfo','niho')
def __init__(self, *args, **kwargs):
munc = kwargs.pop('munc')
super(TentForm, self).__init__(*args, **kwargs)
self.fields['primary'].queryset = Mark.objects.filter(munc=munc)
class TentCreate(CreateView):
form_class = TentForm
def get_form(self, form_class=None):
if form_class is None:
form_class = self.get_form_class()
kwargs = self.get_form_kwargs()
print(kwargs, self.kwargs)
kwargs.update(self.kwargs)
return form_class(**kwargs)
forms.py
class TentForm(ModelForm):
class Meta:
model = Tent
exclude =('asfo','niho')
def __init__(self, *args, **kwargs):
munc=self.kwargs['munc']
super(TentForm, self).__init__(*args, **kwargs)
self.fields['primary'].queryset = Mark.objects.filter(munc=munc)
you must pop munc before call super(TentForm, self).__init__(*args, **kwargs)
I want for filter a ModelChoiceField with the current user. I found a solution very close that I want to do, but I dont understand
Django: How to get current user in admin forms
The answer accepted says
"I can now access the current user in my forms.ModelForm by accessing self.current_user"
--admin.py
class Customer(BaseAdmin):
form = CustomerForm
def get_form(self, request,obj=None,**kwargs):
form = super(Customer, self).get_form(request, **kwargs)
form.current_user = request.user
return form
--forms.py
class CustomerForm(forms.ModelForm):
default_tax = forms.ModelChoiceField(queryset=fa_tax_rates.objects.filter(tenant=????))
class Meta:
model = fa_customers
How do I get the current user on modelchoice queryset(tenant=????)
How do I call the self.current_user in the modelform(forms.py)
Override __init__ constructor of the CustomerForm:
class CustomerForm(forms.ModelForm):
...
def __init__(self, *args, **kwargs):
super(CustomerForm, self).__init__(*args, **kwargs)
self.fields['default_tax'].queryset =
fa_tax_rates.objects.filter(tenant=self.current_user))
Queryset in the form field definition can be safely set to all() or none():
class CustomerForm(forms.ModelForm):
default_tax = forms.ModelChoiceField(queryset=fa_tax_rates.objects.none())
Just to sum up the solution because it was very hard for me to make this work and understand the accepted answer
In admin.py
class MyModelForm (forms.ModelForm):
def __init__(self, *args,**kwargs):
super (MyModelForm ,self).__init__(*args,**kwargs)
#retrieve current_user from MyModelAdmin
self.fields['my_model_field'].queryset = Staff.objects.all().filter(person_name = self.current_user)
#The person name in the database must be the same as in Django User, otherwise use something like person_name__contains
class MyModelAdmin(admin.ModelAdmin):
form = MyModelForm
def get_form(self, request, *args, **kwargs):
form = super(MyModelAdmin, self).get_form(request, *args, **kwargs)
form.current_user = request.user #get current user only accessible in MyModelAdminand pass it to MyModelForm
return form
I would like to return to the User related record.
Somebody can help me?
part of my view
class UserProfileDetailView(DetailView):
model = get_user_model()
slug_field = "username"
template_name = "perfil.html"
def get_object(self, queryset=None):
user = super(UserProfileDetailView, self).get_object(queryset)
UserProfile.objects.get_or_create(user=user)
return user
Something like a old way>
def my_view(request, slug):
var = get_object_or_404(Model, slug=slug)
xxx = AnotherModel.objects.filter(var=var)
...
how can i perfome this in the first view UserProfileDetailView,
show related data?
What I do in this case is add the related object into the context data. It would be something like this:
class UserProfileDetailView(DetailView):
model = get_user_model()
slug_field = "username"
template_name = "perfil.html"
def get_context_data(self, **kwargs):
# xxx will be available in the template as the related objects
context = super(UserProfileDetailView, self).get_context_data(**kwargs)
context['xxx'] = AnotherModel.objects.filter(var=self.get_object())
return context
Another approach is to extend DetailView with MultipleObjectMixin, as in this example:
from django.views.generic import DetailView
from django.views.generic.list import MultipleObjectMixin
from django.contrib.auth import get_user_model
class DetailListView(MultipleObjectMixin, DetailView):
related_model_name = None
def get_queryset(self):
# a bit of safety checks
if not hasattr(self, "related_model_name"):
raise AttributeError(
"%s.related_model_name is missing." % (
self.__class__.__name,))
if not self.related_object_name:
raise NotImplementedError(
"%s.related_model_name must not be None." % (
self.__class__.__name,))
# get the object
obj = self.get_object()
# get the related model attached to the object
related_model = getattr(obj, "%s_set" % self.related_model_name, None)
# safety check if related model doesn't exist
if not related_model:
raise AttributeError(
"%s instance has no attribute \"%s_set\"" % (
obj.__class__.__name__, self.related_model_name)
# return the related model queryset
return related_model.all()
def get(self, request, *args, **kwargs):
self.object_list = self.get_queryset()
return super(DetailListView, self).get(request, *args, **kwargs)
class UserProfileDetailView(DetailListView):
template_name = "perfil.html"
model = get_user_model()
slug_field = "username"
related_model_name = "anothermodel"
def get_object(self, queryset=None):
user = super(UserProfileDetailView, self).get_object(queryset)
UserProfile.objects.get_or_create(user=user)
return user
In my opinion this approach is a little less cleaner and understandable, but it has a huge advantage in reusability. It definitely has one downside: if you are using the class variable context_object_name, it will refer to the related objects list and not to the object itself (this has to do with how the inheritance chain is set up when constructing the class).
I'm trying to update a model in Django using the class-based generic view UpdateView.
I read the page Updating User model in Django with class based UpdateView to try and get me started, but I'm getting an error 'WSGIRequest' object has no attribute 'id'
I'm a fresh face to Django, so please be forgiving if I'm doing something stupid.
//urls.py
url(r'^portfolios/update/(?P<id>\d+)/$',PortfoliosUpdateView.as_view()),
//views.py
class PortfoliosUpdateView(UpdateView):
form_class = PortfoliosCreateForm
model = Portfolios
template_name = 'portfolios/create.html'
def get(self, request, **kwargs):
self.object = Portfolios.objects.get(id=self.request.id)
form_class = self.get_form_class()
form = self.get_form(form_class)
context = self.get_context_data(object=self.object, form=form)
return self.render_to_response(context)
def get_object(self, queryset=None):
obj = Portfolios.objects.get(id=self.request.id)
return obj
It's mostly just a modified version of the code originally posted, but I thought it'd work. I know that I'm trying to retrieve the id passed as a GET parameter, but that doesn't seem to come through in the request variable. Am I going about this the wrong way?
Thanks
Edit: I think I fixed it, but this may be wrong:
I changed the lines
self.object = Portfolios.objects.get(id=self.request.id)
obj = Portfolios.objects.get(id=self.request.id)
to
self.object = Portfolios.objects.get(id=self.kwargs['id'])
obj = Portfolios.objects.get(id=self.kwargs['id'])
I could be wrong.
It should be:
def get_object(self, queryset=None):
obj = Portfolios.objects.get(id=self.kwargs['id'])
return obj
Look at class based generic view dispatch explains that keyword arguments are assigned to self.kwargs.:
def dispatch(self, request, *args, **kwargs):
# Try to dispatch to the right method; if a method doesn't exist,
# defer to the error handler. Also defer to the error handler if the
# request method isn't on the approved list.
if request.method.lower() in self.http_method_names:
handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
else:
handler = self.http_method_not_allowed
self.request = request
self.args = args
self.kwargs = kwargs
return handler(request, *args, **kwargs)
id = self.request.GET.get('id',None) is what you needed when trying to access the GET query string.
However, your view can be simplified:
from django.conf.urls import *
from django.views.generic import UpdateView
from yourapp.models import Portfolios
from yourapp.forms import PortfoliosCreateForm
urlpatterns = patterns('',
url('^portfolios/update/(?P<pk>[\w-]+)$', UpdateView.as_view(
model=Portfolios,
form_class=PortfoliosCreateForm,
template_name='portfolios/create.html',
success_url='/portfolios'
), name='portfolio_update'),
)
views.py
class MyUpdateView(UpdateView):
model = ModelName # required
template_name = 'x/h1.html'
form_class = ModelNameForm
success_url = reverse_lazy('app:page1')
def get_queryset(self):
"""
Optional condition to restrict what users can see
"""
queryset = super().get_queryset()
return queryset.filter(id__lt=20)
def get_success_url(self):
return reverse_lazy(
'app1:abc',
kwargs={'pk': self.object.id}
)
urls.py
In urlpatterns=[]
path('xyz/<pk>/', MyUpdateView.as_view(),name='xyz')
my_model_view.html
{{form}}
You will be able to edit ModelName at url /xyz/<pk>/ where <pk> can be anything from 1 to 20 based on our condition in get_queryset(). Take that condition out to allow users to edit any object.
self.object is only available after post request to the UpdateView.