Editing an Object using a form - django-models

Hey everyone i'm new to Django so i'm having problems figuring this out.
I currently have a form that allows a user to save his/her events. I want to also have the user be able to click on the event and edit the event without creating a new event. This is the form i have currently that add events.
def event_page(request):
if request.method == 'POST':
form = EventForm(request.POST)
if form.is_valid():
event = Event.objects.create(
user=request.user,
eventname = form.cleaned_data['eventname'],
eventdate = form.cleaned_data['eventdate'],
eventtime = form.cleaned_data['eventtime'],
address = form.cleaned_data['address'])
event.save()
return HttpResponseRedirect(
'/user/%s/' % request.user.username
)
else:
form = EventForm()
variables = RequestContext(request, {
'form': form
})
return render_to_response('events.html', variables)
Can anyone help to make this form so it also allows editing?
Thanks!

This is quite easy if you're using a ModelForm (http://docs.djangoproject.com/en/1.3/topics/forms/modelforms/)
Sample code would look something like this:
def event_page(request, event_id=None):
"""Return a form allowing the user to create or edit
an event. On a POST request, process the form."""
# get an event instance: use a dummy instance if
# we're making a new event, else retrieve the requested event
if event_id:
event = get_object_or_404(Event, id=int(event_id))
else:
event = Event()
# am I processing the form? if so, handle that...
if request.method == 'POST':
form = EventForm(request.POST, instance=event)
if form.is_valid():
# save the form contents to the database
event = form.save()
# done; redirect the user off
return HttpResponseRedirect('/user/%s/' % request.user.username)
else:
form = EventForm(instance=event)
# done; return the template
return render_to_response('events.html', RequestContext(request, {
'form': form,
}))

Related

Update model instance by another user but keep originator name unchanged in Django

I am learning and working on particular project, where I am allowing few users in the same model (initiator, reviewer and approver) to update model by using model.forms during the different state (initiation (draft), review and approval).
I have no issue on initiation stage as the originator is the same user who does model update. problem which I am struggling appears to happen during review and approval stages which makes my model updated however originator name is replaced by another user if I call:
def update_eq(request, pk):
___rest of the code___
if eq.eq_status == 'Draft':
form = EqForm(request.POST or None, instance=eq)
if form.is_valid():
eq.initiator = request.user
form.save()
___rest of the code___
[Solution] Based on Willem's comment my updated view...
elif eq.eq_status == 'Review':
form = EqForm(request.POST or None, instance=eq)
if form.is_valid():
# eq.initiator = request.user
form.save()
___rest of the code___
However, once I change the state i.e. move to Review or Approve stage and making same update i.e. calling to save form, I am getting the initiator field updated with 'None' value.
Is there any way I allow non-initiator to update the model, but keep model original initiator as current initiator still?
You are editing the Eq instance, so that means the instance is already created and thus the initiator does not need to be set/updated. You thus can remove the eq.initiator = request.user line(s) in your view:
def update_eq(request, pk):
# rest of the code …
if eq.eq_status == 'Draft':
form = EqForm(request.POST or None, instance=eq)
if form.is_valid():
# no eq.initiator = request.user
form.save()
return redirect('name-of-some-view')
# …
Note: In case of a successful POST request, you should make a redirect
[Django-doc]
to implement the Post/Redirect/Get pattern [wiki].
This avoids that you make the same POST request when the user refreshes the
browser.

Signal not being triggered on .save method

I want to create a streaming API(server sent events) for alerts i.e new added alerts should be sent to open connections as server sent events. For that, i am using StreamingHttpResponse. Header is set to text/event-stream as it should be for SSEs. I have a model that handles alerts in DB. I have written a signals.py file that defines signal for post_save for Alert model and for post_save event on Alert model, I return StreamingHttpResponse with newly added alert. When I got the API first time, I get response with all alerts but not getting new added alert/s when ever a new alert is added to DB. What am I doing wrong?
monitoring/signals.py
#receiver(post_save, sender=Alert)
def send_alert(sender, instance, **kwargs):
qs_json = serializers.serialize('json', instance)
return StreamingHttpResponse(qs_json, content_type='text/event-stream')
monitoring/views.py
# just importing defined signal receiver here
from .signals import send_alert
# endpoint view function
def get_alerts(self):
queryset = Alert.objects.all().order_by('-date')
resolved = self.request.query_params.get('resolved')
server = self.request.query_params.get('server')
source = self.request.query_params.get('source')
if source is not None:
queryset = queryset.filter(source=source)
if resolved is not None:
queryset = queryset.filter(resolved=resolved)
if server is not None:
queryset = queryset.filter(server=server)
return HttpResponse(queryset)
Even when I hit the .save method on DB myself on Django shell, signal is not triggered.
>>> from monitoring.models import Alert
>>> a=Alert(container="something")
>>> s.save()

Django REST API custom methods for generic views

I'm intern and work on a project where I develop DRF API that need to interact with mobile app written by my colleague with Ionic framework.
We are creating new user. My view method is following:
class NewUser(generics.CreateAPIView):
model = User
permission_classes = [permissions.AllowAny]
serializer_class = NewUserSerializer
def create(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
self.perform_create(serializer)
headers = self.get_success_headers(serializer.data)
token, created = Token.objects.get_or_create(user=serializer.instance)
return Response({'token': token.key}, status=status.HTTP_201_CREATED, headers=headers)
When someone wants to create new user via POST request if user name has't been taken yet, then API return 201 status code and token in JSON, if user name already taken it returns 400 status and error message in JSON. MY colleague request me to change status message to 200 when he tries to create username with name that already exist. He says that he can't consume the ERROR response.
His code looks like:
$http.post(url,{
username:$scope.tel,
password:$scope.passwd
}).success(function(data){
alert(data);
$ionicLoading.hide();
console.log(data);
})
Question:
1) Should I tweak my API to send 200 status instead of more logical 400 for 'user already register' error?
I tried to change my code, But i couldn't find the method to override in CreateAPIView/ModelSerializer of DRF. I ended up rewriting my view class to method:
#api_view(['POST'])
def newUser(request):
"""
Saves a new user on the database
"""
if request.method == 'POST':
serializer = NewUserSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
token, created = Token.objects.get_or_create(user=serializer.instance)
return Response({'token': token.key}, status=status.HTTP_201_CREATED, headers=serializer.data)
else:
return Response(serializer.errors, status=status.HTTP_200_OK)
Question:
2) If I want to change behaviorof API and responce, which method should I override
3) I'm new to Django and still don't qiute know where we should use generic views VS. #.... methods
200 vs 400 in this case is mostly preference. 400 means "Bad Request". This is generally more correct for a incorrectly formatted request, rather than one that doesn't meet some condition.
200 is just as appropriate it conveys the correct information:
Your request was valid, but I didn't create a new record.
As to how to do the override. The shortest path is to override the CreateAPIView.create and change the response code used. You should also avoid repeating the default behavior of CreateAPIView by calling super.
class CreateUserView(generics.CreateAPIView):
model = User
permission_classes = [permissions.AllowAny]
serializer_class = NewUserSerializer
def create(self, request, *args, **kwargs):
response = super(CreateUserView, self).create(request, *args, **kwargs)
token, created = Token.objects.get_or_create(user=serializer.instance)
response.status = status.HTTP_200_OK
response.data = {'token': token.key}
return response
Personally, I would have also crafted my NewUserSerializer to have a token field and handle the token so I didn't have to do that work in the View. It doesn't belong in a View.
Save and deletion hooks:
The following methods are provided by the mixin classes, and provide easy overriding of the object save or deletion behavior.
perform_create(self, serializer) - Called by CreateModelMixin when
saving a new object instance. perform_update(self, serializer) -
Called by UpdateModelMixin when saving an existing object instance.
perform_destroy(self, instance) - Called by DestroyModelMixin when
deleting an object instance.
These hooks are particularly useful for setting attributes that are implicit in the request, but are not part of the request data. For instance, you might set an attribute on the object based on the request user, or based on a URL keyword argument.
https://www.django-rest-framework.org/api-guide/generic-views/#methods
class CourseOrder(generics.CreateAPIView):
serializer_class = serializers.OrderCoursesSerializer
permission_classes = [permissions.AllowAny]
# hook before creating
def perform_create(self, serializer):
# print(serializer['name'].value)
# save post data
serializer.save()
try:
subject, from_email, to = 'New order', 'zelenchyks#gmail.com', 'zelenchyks#gmail.com'
text_content = 'New order'
html_content = '''
<p>client name: %s </p>
<p>client phone: %s </p>
'''
% (serializer['name'].value, serializer['mobile'].value)
msg = EmailMultiAlternatives(subject, text_content, from_email, [to])
msg.attach_alternative(html_content, "text/html")
msg.send()
except Warning:
print('Huston we have a problems with smtp')

Django generic UpdateView in Djangular

The Python source code for Djangular Demos gives examples of how to process the post request from a form that creates a Django object instance. But they don't show how to process the post request from a form which updates an existing object instance.
The code for updating an object seems rather complicated: my code is missing something crucial. Using my code I always get a form validation error: Object with this Name already exists.
I am using the Django generic UpdateView class and my model has a unique field called name.
My code:
from django.views.generic.edit import UpdateView
class MyForm(NgModelFormMixin, Bootstrap3FormMixin, NgModelForm):
scope_prefix='form_data'
form_name = 'my_form'
class Meta:
model = models.MyModel
fields = ['name','person']
class MyModelUpdate(UpdateView):
model = models.MyModel
form_class = MyForm
def post(self, request, **kwargs):
if request.is_ajax():
return self.ajax(request, **kwargs)
return super(MyModelUpdate, self).post(request, **kwargs)
# from the djangular combined_validation example
def ajax(self, request, **kwargs):
# tbd: need update-specific logic here: pass in instance
# parameter (object) or set it from pk. Base class post
# methods use self.get_object()
form = self.form_class(data=json.loads(request.body))
return JsonResponse({'errors': form.errors,
'success_url': force_text(self.success_url)})
What code do I need to get Django to load the instance identified by the pk argument and attach it to the form. That would be the default behavior when the request data comes from POST rather than ajax?
After trial and error experimentation I came up with the following new implementation for the view's ajax method. It passes my tests but feels clunky.
def ajax(self, request, **kwargs):
form = self.form_class(data=json.loads(request.body),
instance=self.get_object())
try:
form.save()
except:
# error is in form.errors
pass
return JsonResponse({'errors': form.errors,
'success_url': force_text(self.success_url)})

GAE put object, redirect, then query for object returns null?

All,
I am in the process of learning Google App Engine / Webapp2 and i'm having trouble saving an object to the datastore, redirecting to another page/handler, then fetching that object from the datastore. Forgive me if there is an easy answer to this question. The following is a description of the code I have.
I have a base handler:
class BaseHandler(webapp2.RequestHandler):
def set_secure_cookie(self, name, val):
cookie_val = make_secure_val(val)
self.response.headers.add_header(
'Set-Cookie',
'%s=%s; Path=/' % (name, cookie_val))
def get_secure_cookie(self, name):
cookie_val = self.request.cookies.get(name)
return cookie_val and check_secure_val(cookie_val)
def login(self, user):
self.set_secure_cookie('user', str(user.name))
# Called before every request and stores user object
def initialize(self, *a, **kw):
webapp2.RequestHandler.initialize(self, *a, **kw)
username = self.get_secure_cookie('user')
self.user = username and User.by_name(str(username))
I have a Signup page which inherits from BaseHandler:
class Signup(BaseHandler):
def get(self):
# Get the page
def post(self):
has_error = False
# Extract and validate the input
if has_error:
#Re-render the form
else:
new_user = User.register(self.username, self.password, self.email)
new_user.put()
self.login(new_user)
self.redirect("/blog/welcome")
If the user is a new user, the User db.Model object is created, the user is stored to the datastore, a user cookie is set and we are redirected to the Welcome handler:
class Welcome(BaseHandler):
def get(self):
if self.user:
self.render('welcome.html', username = self.user.name)
else:
self.redirect('/blog/signup')
The intent here is that upon redirect, BaseHandler.initialize() would get called and would set self.user of the new user I just created.
Here is what I know:
- When signing up a new user, I am redirected back to the signup page.
- If I then manually navigate to /blog/welcome, the page loads correctly with the new username populated.
If I add the following logging statements into Welcome.get():
username = self.get_secure_cookie('user')
logging.info("Cookie %r obtained inside of Welcome.get().", username)
logging.info("Found user %r", User.by_name(str(username)))
The cookie is obtained for the new username but no User object is found. Again, if I navigate directly to /blog/welcome, the logs report that the cookie is obtained and the User object is found for the new user.
The User object looks like so:
def users_key(group = 'default'):
return db.Key.from_path('users', group)
class User(db.Model):
name = db.StringProperty(required = True)
password = db.StringProperty(required = True)
email = db.StringProperty()
#classmethod
def by_name(cls, name):
u = User.all().filter('name =', name).get()
return u
#classmethod
def register(cls, name, password, email = None):
return User(parent = users_key(),
name = name,
password = password,
email = email)
Is there something about the datastore that is causing this first query to get the new user to return nothing? How should I proceed in debugging this? Is there additional reading I should do? (I have tried to provide all necessary code snippets but I can provide additional code if required.)
Just guessing, but I suspect self.username, self.password and self.email in your RequestHandler are not set to anything. I'm assuming you're getting those paramters from the request POST data, but that's not happening in the code shown.
The other potential problem is that your query is eventually consistent, and may not reflect recent changes (ie new User entity). It would be much better if you fetch the user by it's key or id with a get() call instead of a query via filter().

Resources