Get single object in Django rest framework / Angular2 buggs after login - django

Im building Django + Angular2 project.
I have side bar and need to add Settings there, which will route to /users/{{ user.id }}, at the moment it works with bug, that when I login, this:
<li *ngFor="let user of users"><a routerLink="/users/{{ user.id }}" style="cursor: pointer">Settings</a></li>
will not show because for loop will not have any data. If I refresh page it will show in sidebar and will work perfect. Main idea is to open settings for authorized user.
This is how I get users:
getUsers() {
let headers = new Headers();
headers.append('Content-Type', 'application/json');
let authToken = localStorage.getItem('auth_token');
headers.append('Authorization', `JWT ${authToken}`);
return this.http
.get(this.apiURL, { headers })
.toPromise()
.then(res => res.json());
}
Problem is that I need to use for loop because in django framework I filter objects and get list of items. Actually I filter them to get only one, user who is logged in at the moment (code from views.py):
class UserViewSet(viewsets.ModelViewSet):
serializer_class = UserSerializer
def get_queryset(self):
user = self.request.user
queryset = User.objects.filter(id=user.id)
return queryset
queryset = User.objects.filter(id=None)
Is there way to get only one object not list of data to not use for loop in angular (maybe that will fix the bug). Or maybe you can explain me and say how to fix that thing, that Settings disappears and shows only after refresh. Actually I think that for sure I use getUsers function in wrong component or wrong way.
My app.component.ts:
ngOnInit(): void {
this.userService
.getUsers()
.then(users => this.users = users)
}
I don't know for sure where is problem and why after login for loop don't have data, so I don't know what peace of code you need to help me. If need more code just ask.

I found solution, problem was that I used ngOnInit() and it loads on first time when user isn't logged in and gives to html empty variable so it can't loop.
At the moment I use ngDoCheck() which check if is session.
Example:
this.isSession = localStorage.getItem('auth_token');
if ((this.isSession != null) && (this.i == false)){
this.userService
.getUsers()
.then(users => this.users = users);
this.i = true;
}
}

Related

AngularJS/Django Post Response Data

I'm using AngularJS for the front-end and Django for the backend of a web app I'm working on. Right now I'm working on logging in users and I'm having a strange problem. Heres the relevant Angular code:
app.factory('AuthService', ["$http", "$q", "Session", "URL", function($http, $q, Session, URL) {
return {
login: function(credentials) {
var deferred = $q.defer();
$http.post(URL.login, credentials)
.then(function(data, status, headers, config) {
data=data.data; //WHY DOES THIS WORK?
if (data.success == true) {
alert("logged in");
Session.create(credentials.username, data.api_key);
deferred.resolve();
}
else {
deferred.reject("Login failed!");
}
}, function(data, status, headers, config) {
deferred.reject("Login failed!");
});
return deferred.promise
},
And here is the corresponding Django view:
def login_user(request):
'''
Given a username and password, returns the users API key.
'''
if request.method == 'POST':
username = request.POST.get('username',None)
password = request.POST.get('password',None)
user = authenticate(username=username,password=password)
if user is not None:
api_key = ApiKey.objects.get(user=user)
response_data = {}
response_data["api_key"] = str(api_key).split(" ")[0]
response_data["success"] = True
return HttpResponse(json.dumps(response_data), content_type="application/json")
else:
return HttpResponse(json.dumps({"username":username,"success":False}),content_type="application/json")
return HttpResponseBadRequest()
When the user logs in a POST request is sent and handled by the above Django code. The response is then picked up by the AngularJS code above. As you can see the then() method in the Angular code takes the usual four parameters: data, status, config and headers. I expect to see data contain the dictionary output from the Django code, appropriately serialized into a JSON object.
However what happens is that the only parameter of the then() method which is not undefined is data, and this contains EVERYTHING; headers, data, status code,etc.
The line commented 'WHY DOES THIS WORK' fixes the problem, by accessing the data inside. However, I want to know why this is happening and if there is any way to avoid this. My best guess is that it has something to do with the way Django serializes a response but I'm not sure.
I'm using Django 1.6.5.
That is actually how Angular promises work according to the docs. Here is the relevant quote.
Since the returned value of calling the $http function is a promise,
you can also use the then method to register callbacks, and these
callbacks will receive a single argument – an object representing the
response. See the API signature and type info below for more details.
The emphasis was mine.

Using FullCalendar with Django Views

I'm trying to implement the FullCalendar on my website, but am a little new to this and not quite sure how to format what I want to do. I have a view which will grab all of an individual user's events. I want to take those events and populate the calendar with them. My issue is that I don't know what to return in the view or how to handle that return value in the JavaScript function. Here's what I have right now:
View:
def calEvents(request):
user = request.user.get_profile()
eventList = user.eventList
ownedList = user.ownedEvent
events = #Part I'm having trouble with
return HttpResponse(events)
The eventList and ownedEvent keep track of all of a user's events. They have names/dates associated with them. What I don't understand is the format I need to put all that information in to return in my HttpResponse.
My JavaScript function is:
$(document).ready(function() {
$('#calendar').fullCalendar({
eventSources: [
{
url: '/calEvents/',
editable: false,
}
]
});
});
I'm telling it to go to the Django view, but am lost after that. Thanks so much in advance!
I have done this by building a list of dictionaries in my Django view, setting at minimum the required fields of 'title' and the start time, then using simplejson.dumps with cls=DjangoJSONEncoder to return json.
from django.core.serializers.json import DjangoJSONEncoder
def calEvents(request):
# as above, then:
events = []
for event in eventList:
events.append({'title': event.name, 'start': event.start})
# something similar for owned events, maybe with a different className if you like
return HttpResponse(simplejson.dumps(events, cls=DjangoJSONEncoder), mimetype='application/json')
You may also with to limit the events you return based on the starting and ending times provided by the get request:
def calEvents(request):
user = request.user.get_profile()
start_timestamp = request.GET.get('start')
end_timestamp = request.GET.get('end')
start_datetime = datetime.datetime.fromtimestamp(float(start_timestamp))
end_datetime = datetime.datetime.fromtimestamp(float(end_timestamp))
eventList = user.eventList.filter(start_time__lte=end_datetime, end_time__gte=start_datetime)
I am neglecting error handling for the timestamp conversion - fullcalendar will give you appropriate values, but it would be best to allow for the possibility of bad input. And I am making assumptions about the structure of your event models.

Complex forms with Django

Consider the following survey question:
Do you like this question? (choose one)
Yes
No, because
I don't like its formatting
I don't like the wording
Other reason: ...................
Other response (please specify) ......................
I'm trying to represent a series of questions, some of which are like that. Some are more simple (just a list of choices). But I'm having some problems trying to square this off with Django and its own way of doing forms. We have the following problems:
We need server side validation. Only one choice can be specified. And in the case of the "other" choices above, those need a follow-up charfield.
We need to squeeze a charfield into the options! I'm fairly sure I can hack these in via the templates but keep that in mind.
Just to complicate things, the questions and their answers need to be editable. I've done this with YAML already and to the point of generating the form, that works fine.
So what's the best way to hack the Django form system to allow me to do this? Should I bother with django.forms at all or just write something that does everything in its own way? How would you do this?
If you want to avoid dealing with forms I suggest using jquery's $.ajax() method. Basically, you simply need to create a blank form model to catch the POST, then you can grab the data and do with it what you want. Here is an example:
#models.py
class BlankForm(forms.Form):
def __unicode__(self):
return "BlankForm"
#views.py
def my_view(request):
if request.method == 'POST':
if 'answer' in request.POST:
form = BlankForm(request.POST)
if form.is_valid():
foo = request.POST.__getitem__('add')
bar = request.POST.__getitem__('bar')
baz = request.POST.__getitem__('baz')
#Do stuff with your data:
return HttpResponse('ok')
Then in your webpage you could some something like this:
<script type="text/javascript">
function getCookie(name) {
var cookieValue = null;
if (document.cookie && document.cookie != '') {
var cookies = document.cookie.split(';');
for (var i = 0; i < cookies.length; i++) {
var cookie = jQuery.trim(cookies[i]);
// Does this cookie string begin with the name we want?
if (cookie.substring(0, name.length + 1) == (name + '=')) {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
}
}
return cookieValue;
}
function csrfSafeMethod(method) {
// these HTTP methods do not require CSRF protection
return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}
var csrftoken = getCookie('csrftoken');
$.ajaxSetup({
crossDomain: false, // obviates need for sameOrigin test
beforeSend: function(xhr, settings) {
if (!csrfSafeMethod(settings.type)) {
xhr.setRequestHeader("X-CSRFToken", csrftoken);
}
}
});
$.ajax({
type: 'POST',
data: {
'answer': true,
'foo': foo,
'bar': bar,
'baz': baz
},
dataType: 'application/json'
});
}
</script>
All the stuff about the cookies and CSRF token have to do with django's CSRF protection system. Basically all you would need to worry about would be editing the Data field in the $.ajax() method
The various options would be a ForeignKey to another table containing the responses. You'd have to have multiple fields in that FK to hold both the main answer and the subanswer, and you'd have to regroup (or reorder, if you need an explicit order) apropriately. You'd also need a CharField in order to hold the "other" response. It might be easiest to encapsulate both fields in a custom field, and use a custom widget to display the controls in the form.

Django- Many to Many field querying

I have following structure of models in django :
class BodySubPart(models.Model):
body_subpart=models.CharField(max_length=20)
def __str__(self):
return self.body_subpart
class BodyPart(models.Model):
body_part=models.CharField(max_length=20)
body_subpart=models.ManyToManyField(BodySubPart)
def __str__(self):
return self.body_part
Ex:
example,
if BodyPart=head then BodySubPart= face,mouth,eyes,nose.
if BodyPart=arm then BodySubPart= shoulder,fingers,elbow.
like this many body parts are stored.
...
now I want to create a runtime form have two choicefields (BodySubPart and BodyPart) such that when i select the BodyPart it should change the list in BodySubPart.
Ex.
The first choicefield has body parts={head,arm,chest...}
The second choice field should change when i select a particular part
If i select "head" then second choice field should show,
body sub parts={face,mouth,eyes,nose...}
Please help me here.....
What have you tried?? I think you will find people are more willing to help you if you have actually tried something yourself and not just want others to do it for you. It should go something like this:
1) BodyPart.objects.all() # all body parts
2) head = BodyPart.objects.get(body_part='head')
head_subparts = head.body_subpart.all() # all head subparts
django does a great job of explaining how to query these relationships.
https://docs.djangoproject.com/en/dev/topics/db/models/#many-to-many-relationships
In addition there are a number of really great tutorials online regarding djangos' manytomany relationships.
This requires a bit of AJAX, so first step is to create a view to handle that:
from django.core import serializers
from django.http import HttpResponse, HttpResponseBadRequest
from django.shortcuts import get_list_or_404
def ajax_get_bodysubparts(request):
bodypart_id = request.GET.get('bodypart_id')
if bodypart_id:
bodysubparts = get_list_or_404(BodySubPart, bodypart__id=bodypart_id)
data = serializers.serialize('json', bodysubparts)
return HttpResponse(data, mimetype='application/json')
else:
return HttpResponseBadRequest()
Tie that to some URL in urls.py. Then, some JavaScript for your form (assumes jQuery):
$(document).ready(function(){
$('#id_bodypart').change(function(){
var selected = $(this).val();
if (selected) {
$.getJSON('/url/to/ajax/view/', {
'bodypart_id': selected
}, function (data, jqXHR) {
options = [];
for (var i=0; i<data.length; i++) {
options.append('<option value="'+data[i].id+'">'+data[i].body_subpart+'</option>');
}
$('#id_bodysubpart).html(options.join(''));
});
}
});
});
You will probably need a combination of custom form fields and widgets to get what you want.
Check out the django-ajax-filtered-fields project to see if that is close what you are looking for. It will at least provide some guidance if you decide to create your own.
You will need some javascript to make a new request to populate your fields dynamically, so that will also not be available with standard django forms.

Dynamic FilteredSelectMultiple in django-admin

I don't know if this is even possible, any way, I currently have something as the following:
class Incidence(models.Model):
...
instalation = models.ForeignKey('Instalation')
machine = models.ManyToManyField('Machine')
...
class Machine(models.Model):
...
instalation = models.ForeignKey('Instalation')
...
So Machines belongs to instalations and incidences are related to machines and incidences, the idea is to put a dynamic FilteredSelectMultiple widget to select the machines related with the incidence in the admin page. The admin currently is something as:
class IncidenceMachineForm(forms.ModelForm):
filtered_machine = ModelMultipleChoiceField(
queryset=Machine.objects.order_by('hostname'),
required=False, widget=FilteredSelectMultiple("filtered machine name", is_stacked=False)
)
class Meta:
model = Incidence
And then, the modelAdmin uses the form IncidenceMachineForm. The idea is that when you select the instalation of the incidence, only the machines related to that instalation are available for selection. I guess something as this is not possible:
queryset=Machine.objects.filter(instalation=self.instalation).order_by('hostname'),
Any ideas will be highly appreciated. Thanks!
I notice that FilteredSelectMultiple widget has already cached, converted and changed the name of original widget after the page is loaded, so changing the "option" list of "select" tag is not enough.
I came up with this solution:
wrap "select" list inside another element ("div" for instance)
use data received from ajax call to re-create the original list
call "SelectFilter.init" to re-construct the FilteredSelectMultiple widget
Here is the code I have tested:
$('#id_instalation').change(function() {
var selected = $('#id_instalation').val();
if(selected) {
$.ajax({
url: '/url/to/get/machines/' + selected,
success: function(list) {
var options = [];
options.push('<select multiple="multiple" class="selectfilter" name="machine" id="id_machine">');
for(i in list){
options.push('<option value="' + list[i][0] + '">' +
list[i][1] + '</option>');
}
options.push('</select>');
$('#machine_wrapper').html(options.join(''));
// Change title of widget
var title = $('#id_instalation option:selected"').text().toLowerCase();
SelectFilter.init("id_machine", title, 0, "/path/to/django/media/");
},
error: function() {
alert('Server error');
},
});
}
}
This is the sample of data returned from ajax call:
[[1, "Machine 1"], [2, "Machine 2"], [3, "Machine 3"]]
For server side implementation, please see Chris Pratt's answer
Note: tested with:
jquery-1.7.2
django 1.2.5
You can do that after the model has been saved, and there's an instalation associated with it to use (though the lookup would be instalation=self.instance.instalation).
However, that doesn't do you much good, because if a different instalation is selected the list would still be the one for the old selection, and obviously you get no help when first creating the object.
As a result, the only way to accomplish this is with AJAX. You create a view to receive the selected instalation id, and return a JSON response consisting of machines associated with it. Tie the view into your urlconf, and then hit it with AJAX and update the select box based on the results.
from django.http import Http404, HttpResponse
from django.shortcuts import get_object_or_404
from django.utils import simplejson
def ajax_admin_get_machines_for_instalation(request):
instalation_id = request.GET.get('instalation_id')
if instalation_id is None:
# instalation_id wasn't provided so return all machines
machines_qs = Machine.objects.all()
else:
instalation = get_object_or_404(Instalation, pk=instalation_id)
machines_qs = Machine.objects.filter(instalation=instalation)
# 'name' is the field you want to use for the display value
machines = machines_qs.values('pk', 'name')
return HttpResponse(simplejson.dumps(machines), mimetype='application/json')
Then the JS:
(function($){
$(document).ready(function(){
function update_machine_options(){
var selected = $('#id_instalation').val();
if (selected) {
$.getJSON('/url/for/ajax/view/', {
instalation_id: selected
}, function(data, jqXHR){
var options = [];
for (k in data) {
options.append('<option value="'+data[k].pk+'">'+data[k].name+'</option>');
}
$('#id_machine').html(options.join(''));
});
}
}
update_machine_options();
$('#id_instalation').change(function(){
update_machine_options();
});
});
})(django.jQuery);
from django.contrib.admin.widgets import FilteredSelectMultiple
#admin.register(YourModel)
class YourModelAdmin(admin.ModelAdmin):
def formfield_for_manytomany(self, db_field, request, **kwargs):
kwargs['widget'] = FilteredSelectMultiple(
db_field.verbose_name,
False,
)
return super().formfield_for_manytomany(db_field, request, **kwargs)
fast and don't need to override ModelForm or etc.
effect all m2m fields.