Django UpdateView create new object - python

My problem:
UpdateView create new object instead of updating previous, i think its happens because in class definition of my view i override get_object method like this:
def get_object(self, queryset=None):
try:
object_get = self.model.objects.get(pk=self.kwargs['pk'])
except ObjectDoesNotExist:
raise Http404("No object found matching this query")
if self.request.user.is_authenticated():
if object_get.owner == self.request.user:
return object_get
And so if current user is not owner of the object - this method return nothing - its what i wanted, but my form class instead create new object:
class ClientCreation(forms.ModelForm):
class Meta:
model = Client
fields = ('name', 'loyal')
I think this is happened because form doesn't receive a self.instance and create new instead - what should i do in this situation? I don't want new object to be created, in case when owner of object is not the current user i want nothing to happend then sending such a post request. How should i correctly implement this?
UPDATE views.py:
class Distinct(generic.UpdateView):
def get_object(self, queryset=None):
try:
object_get = self.model.objects.get(pk=self.kwargs['pk'])
except ObjectDoesNotExist:
raise Http404("No object found matching this query")
if self.request.user.is_authenticated():
if object_get.owner == self.request.user:
return object_get
def get_form_kwargs(self):
kwargs = super(Distinct, self).get_form_kwargs()
if self.request.user.is_authenticated():
kwargs.update({'user': self.request.user})
return kwargs
def post(self, request, *args, **kwargs):
if request.POST.get('action', '') == 'Delete':
object_get = self.get_object()
request.session['deleted_data'] = str(object_get)
object_get.delete()
return redirect(reverse('crm:main'))
else:
return super(Distinct, self).post(request, *args, **kwargs)
def get_success_url(self):
return reverse('crm:{}'.format(self.distinct_template), kwargs={'pk': self.kwargs['pk']})
class DistinctClient(Distinct):
form_class = ClientCreation
model = Client
template_name = 'crm/client_detail.html'
all_template = 'clients'
distinct_template = 'client'
def get_form_kwargs(self):
return generic.UpdateView.get_form_kwargs(self)

In UpdateView, if get_object returns None django will create a new object.So instead of return None do whatever you want.
def get_object(self, queryset=None):
try:
object_get = self.model.objects.get(pk=self.kwargs['pk'])
except ObjectDoesNotExist:
raise Http404("No object found matching this query")
if self.request.user.is_authenticated():
if object_get.owner == self.request.user:
return object_get
raise My #do something here.
UPDATE
class My(Exception):
pass
class DistinctClient(Distinct):
form_class = ClientCreation
model = Client
template_name = 'crm/client_detail.html'
all_template = 'clients'
distinct_template = 'client'
def dispatch(self, *args, **kwargs):
try:
return super(DistinctClient, self).dispatch(*args, **kwargs)
except My:
return redirect #to do or (return render(self.request, 'mytemplate.html', {}))

Related

Django rest framework self.get_object() crashes my application

#detail_route(methods=['POST', 'DELETE'], permission_classes=[BubbleIsMember])
#parser_classes((FormParser, MultiPartParser,))
def rsvp(self, request, *args, **kwargs):
response_data = {'message' : 'Error'}
try:
post_data = request.data
except:
response_data['message'] = 'Malformed json. We received:' + json.dumps(request.data)
return Response(response_data, status=HTTP_400_BAD_REQUEST)
if 'meeting' not in request.data:
response_data['message'] = "'meeting' field is required"
return Response(response_data, status=HTTP_400_BAD_REQUEST)
try:
meeting = Meeting.objects.get(pk=post_data['meeting'], bubble=self.get_object())
print("self", self)
rsvp, created = MeetingRSVP.objects.get_or_create(user=request.user, meeting=meeting)
self.get_object() after that line; code doesn't get processed.
Below is what my only 'self' looks like, I'm trying to get bubble object from it.
<arrowapi.views.bubble.BubbleViewSet object at 0x1067728d0>
My queryset within that View looks like this:
def get_queryset(self):
if self.request.user.is_superuser:
return Bubble.objects.all()
elif self.request.user.is_authenticated:
memberships = BubbleMembership.objects.filter(user=self.request.user).values_list('bubble', flat=True)
return Bubble.objects.filter(pk__in=memberships)
else:
return Bubble.objects.none()

'QuerySet' object has no attribute '_meta'

i am developing a simple app with python django framework and i am using Class based Views, when i use the UpdateView and try to run my template i get this error;
'QuerySet' object has no attribute '_meta'
This is my view codes
class UpdateStaff(LoginRequiredMixin, UpdateView):
template_name = 'app/update_staff.html'
form_class = UpdateStaffForm
model = Staff
def get_object(self, queryset=None):
obj = Staff.objects.filter(pk=self.kwargs['staff_id'])
return obj
def post(self, request, *args, **kwargs):
form = self.form_class(request.POST or None,
instance=self.get_object())
if form.is_valid():
obj = form.save(commit=False)
obj.save()
messages.success(self.request, "Staff has been updated")
return self.get_success_url()
else:
messages.error(self.request, "Staff not updated")
return HttpResponseRedirect(reverse('update_staff'))
def get_success_url(self):
return HttpResponseRedirect(reverse('manage_staffs'))
def get_context_data(self, **kwargs):
context = super(UpdateStaff,
self).get_context_data()
context['messages'] = messages.get_messages(self.request)
context['form'] = self.form_class(self.request.POST or None,
instance=self.get_object())
return context
and this is my form codes:
class UpdateStaffForm(ModelForm):
class Meta:
model = Staff
exclude = (
'profile_picture', 'start_work', 'last_login', 'groups',
'user_permissions', 'is_active', 'is_superuser',
'date_joined', 'end_work', 'can_sell_out_of_assigned_area',
'is_staff')
def __init__(self, *args, **kwargs):
super(UpdateStaffForm, self).__init__(*args,
**kwargs)
for field_name, field in self.fields.items():
field.widget.attrs['class'] = 'form-control'
Anyone having an idea to solve this please help.
The get_object method returns queryset i.e list of records, instead of instance.To get instance you can use first() on filter() . This will gives you first occurrence.
def get_object(self, queryset=None):
obj = Staff.objects.filter(pk=self.kwargs['staff_id']).first()
return obj
Late answer but worth it.
from django.shortcuts import get_object_or_404
...
def get_object(self, queryset=None):
obj = get_object_or_404(Staff, pk=self.kwargs['staff_id'])
return obj
This approach will return HTTP 404 Not Found response if object not found.

django class based delete view and validation

My view:
class ModEmailDeleteView(DetailView):
model = EmailModel
template_name = "email_delete.html"
success_url = reverse_lazy('moderator_profile', request.user.id)
Here I want to check if the user of particular filed is request user then only he can delete. Like
if obj.user == request.user
then only he can delete or throw 404
Also what if I have multiple primary key in the url?? and want different validations using those primary keys
You can do it like this:
class ModEmailDeleteView(DeleteView):
model = EmailModel
template_name = "email_delete.html"
success_url = reverse_lazy('moderator_profile', request.user.id)
def delete(self, request, *args, **kwargs):
self.object = self.get_object()
if self.object.user == request.user:
self.object.delete()
return HttpResponseRedirect(self.get_success_url())
else:
raise Http404 #or return HttpResponse('404_url')
There is mixin for django Deleview , you just override the delete
function in your view,
class DeletionMixin(object):
"""
A mixin providing the ability to delete objects
"""
success_url = None
def delete(self, request, *args, **kwargs):
"""
Calls the delete() method on the fetched object and then
redirects to the success URL.
"""
if self.object.user = request.user:
self.object.delete()
success_url = self.get_success_url()
self.object.delete()
return HttpResponseRedirect(success_url)

Passing request object from view to form in Django

I'm trying to create an account edit page which visually contains a single form (ie single submit button) but the fields are part of two (or more) different models. I've pieced together a solution from several SO answers and the form loads fine on GET requests but I would now like to conditionally hide/display the terms of service checkbox field based on what url is being accessed. Specifically on registration the TOS should be displayed while it should not on the account edit page. Simplified code looks like so:
# views.py
class _RequestPassingFormView(FormView):
http_method_names = ['get', 'post', 'head', 'options', 'trace']
def get(self, request, *args, **kwargs):
# Pass request to get_form_class and get_form for per-request
# form control.
form_class = self.get_form_class(request)
form = self.get_form(form_class)
return self.render_to_response(self.get_context_data(form=form))
def post(self, request, *args, **kwargs):
# Pass request to get_form_class and get_form for per-request
# form control.
form_class = self.get_form_class(request)
form = self.get_form(form_class)
if form.is_valid():
# Pass request to form_valid.
return self.form_valid(request, form)
else:
return self.form_invalid(form)
def get_form_class(self, request=None):
return super(_RequestPassingFormView, self).get_form_class()
def get_form_kwargs(self, request=None, form_class=None):
return super(_RequestPassingFormView, self).get_form_kwargs()
def get_initial(self, request=None):
return super(_RequestPassingFormView, self).get_initial()
def get_success_url(self, request=None, user=None):
# We need to be able to use the request and the new user when
# constructing success_url.
return super(_RequestPassingFormView, self).get_success_url()
def form_valid(self, form, request=None):
return super(_RequestPassingFormView, self).form_valid(form)
def form_invalid(self, form, request=None):
return super(_RequestPassingFormView, self).form_invalid(form)
class AccountEditView(_RequestPassingFormView):
form_class = AccountEditForm
template_name = 'account_edit.html'
def form_valid(self, request, form):
success_url = self.get_success_url(request, new_user)
try:
to, args, kwargs = success_url
return redirect(to, *args, **kwargs)
except ValueError:
return redirect(success_url)
def get_success_url(selfself,request, user):
return '/account'
#forms.py
class CombinedFormBase(forms.Form):
form_classes = []
def __init__(self, *args, **kwargs):
super(CombinedFormBase, self).__init__(*args, **kwargs)
for f in self.form_classes:
name = f.__name__.lower()
setattr(self, name, f(*args, **kwargs))
form = getattr(self, name)
self.fields.update(form.fields)
self.initial.update(form.initial)
def is_valid(self):
isValid = True
for f in self.form_classes:
name = f.__name__.lower()
form = getattr(self, name)
if not form.is_valid():
isValid = False
# is_valid will trigger clean method
# so it should be called after all other forms is_valid are called
# otherwise clean_data will be empty
if not super(CombinedFormBase, self).is_valid() :
isValid = False
for f in self.form_classes:
name = f.__name__.lower()
form = getattr(self, name)
self.errors.update(form.errors)
return isValid
def clean(self):
cleaned_data = super(CombinedFormBase, self).clean()
for f in self.form_classes:
name = f.__name__.lower()
form = getattr(self, name)
cleaned_data.update(form.cleaned_data)
return cleaned_data
class RegistrationForm(forms.Form):
required_css_class = 'required'
email = forms.EmailField(label=_('E-mail'))
password1 = forms.CharField(widget=forms.PasswordInput,
label=_('Password'))
password2 = forms.CharField(widget=forms.PasswordInput,
label=_('Password (again)'))
"""
Conditionlly display TOS checkbox based on context
"""
def __init__(self, *args, **kwargs):
"""
add in a field for terms of service here if viewing
the registration form
"""
super(RegistrationForm, self).__init__(*args, **kwargs)
class AccountProfileForm(forms.Form):
required_css_class = 'required'
company = forms.CharField(label=('Company Name'))
class AccountEditForm(CombinedFormBase):
form_classes = [RegistrationForm, AccountProfileForm]
This is my first django project so it's possible that this is a completely wrong direction also. If so a hint towards a simpler solution would be appreciated.
As you are using class based view, I think you can try like this:
view:
class _RequestPassingFormView(FormView):
http_method_names = ['get', 'post', 'head', 'options', 'trace']
def get(self, request, *args, **kwargs):
form_class = self.get_form_class()
self.object = None
form = form_class(request_data=request)
return self.render_to_response(self.get_context_data(form=form))
Or like this:
class _RequestPassingFormView(FormView):
http_method_names = ['get', 'post', 'head', 'options', 'trace']
#No need to override the get method in this view.
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
kwargs.update(request_data=self.request)
return kwargs
And form is like this:
class RegistrationForm(forms.Form):
#form fields
def __init__(self, *args, request_data=None, **kwargs):
super().__init__(*args, **kwargs)
print(request_data)
# do other operations
I'm gonna keep it super simple. If you need further details, please refer here:
def my_view(request):
if request.method == 'POST':
# Sending your request info as kwarg.
form = myform(request.POST, user=request.user)
class MyForm(forms.Form):
def __init__(self, *args, **kwargs):
# Recieving it.
self.user = (kwargs.pop('user', None))
super(MyForm, self).__init__(*args, **kwargs)
You may want to consider just having two separate forms. If the only difference is the one on the registration page has a terms of service checkbox, then that form class could inherit from the other class and have the extra form field.

How do I delete a collection in Django Rest Api?

I've been trying for a while now to get 'DELETE' to work when reading a collection in Django Api View. I've been using 'ListCreateAPIView' and that only provides get and post method handlers. Does anyone know how to fix this?
My view:
class NotepadDetail(generics.ListCreateAPIView):
model = Session
serializer_class = SessionSerializer
def get_queryset(self):
user=self.request.user
notepad = self.kwargs['notepad_pk']
return Session.objects.filter(user=user, notepad=notepad)
def pre_save(self, obj):
obj.user = self.request.user
obj.notepad = get_object_or_404(Notepad, user=self.request.user, pk=self.kwargs['notepad_pk'])
Solved it by adding:
def get_object(self, notepad_pk):
try:
return Notepad.objects.get(user=self.request.user, pk=notepad_pk)
except Notepad.DoesNotExist:
raise Http404
def delete(self, request, notepad_pk, format=None):
object = self.get_object(notepad_pk)
object.delete()
return Response(status=status.HTTP_204_NO_CONTENT)
You can use a DestroyModelMixin:
class NotepadDetail(generics.DestroyModelMixin,
generics.ListCreateAPIView):
model = Session
serializer_class = SessionSerializer
def get_queryset(self):
user=self.request.user
notepad = self.kwargs['notepad_pk']
return Session.objects.filter(user=user, notepad=notepad)
def pre_save(self, obj):
obj.user = self.request.user
obj.notepad = get_object_or_404(Notepad, user=self.request.user, pk=self.kwargs['notepad_pk'])
def delete(self, request, *args, **kwargs):
return self.destroy(request, *args, **kwargs)
The self.destroy method provided by the DestroyModelMixin will handle the object deletion, will correctly raise the pre_delete and post_delete signals, and will return the 204 NO CONTENT status.
def delete(self, request, pk, format=None):
event = self.get_object(pk)
event.delete()
return Response("Object Deleted")

Categories