Using setattr() to update an object instance - javascript

I have a model class which has an attribute that refers to django DB objects. I would like to change this attribute using a single view with setattr() which I use to make changes to any attributes for this object.
The problem is, I can't seem to pass an object instance through the stack. I'm not sure if I can even use setattr() for this. Actually I'm not even sure if the problem is with my attempted use of setattr() or something else - please let me know!
Error on POST attempt:
ValueError at /dollhouseupdate/1
Cannot assign "u'Citadel'": "Dollhouse.dh_background" must be a "Background" instance.
Model:
class Dollhouse(models.Model):
dollhouse_name = models.CharField(max_length=100)
user = models.ForeignKey(User)
dh_background = models.ForeignKey(Background)
def __str__(self):
return self.dollhouse_name
Template:
<select id="background-select">
<option value="null">Change Background</option>
{% for background in background_objects %}
<option value="{{ background }}">{{ background.bg_name }} </option>
{% endfor %}
</select>
View:
def dollhouseupdate(request, dollhouseid):
if request.method == 'POST':
workingdollhouse = Dollhouse.objects.get(id=dollhouseid)
if request.POST.get('erase') == "true":
workingdollhouse.delete()
return HttpResponse("Dollhouse deleted!")
else:
data = (request.POST).dict()
for key, value in data.items():
setattr(workingdollhouse, key, value)
workingdollhouse.save()
return HttpResponse("Dollhouse {} saved!".format(workingdollhouse.dollhouse_name))
Javascript:
//change dollhouse background
$("#background-select").change(function() {
if($("#background-select").val() != null) {
var dollhouseid = workingDollhouse;
var dh_background = $("#background-select").val()
console.log("changing background to " + dh_background);
$.ajax("http://127.0.0.1:8000/dollhouseupdate/"+dollhouseid, {
type: 'POST',
data: {
dh_background: dh_background,
}
})
.done(function(response){
console.log("The request is complete!");
console.log(response);
window.location = "http://127.0.0.1:8000/";
})
.fail(function() {
console.log("Sorry, there was a problem!");
})
};
});

You are passing the object id in the POST variable, not the actual object itself (you can't do it anyway). Either change the following part
data: {
dh_background: dh_background,
}
to
data: {
dh_background_id: dh_background,
}
or get the object instance using the id in your view code.

As the error says, the Dollhouse.dh_background attribute must be an instance of the Background model. You are attempting to set its value to an object of a different type; I think a text string.
type(u'Citadel') is Background # False
Instead, you'll need to put some smarts into the view so that Background instances are retrieved by whatever key you have; then, set the Dollhouse.dh_background attribute to that instance.
if name == 'background':
background_code = post_args[name]
background = Background.objects.get(code=background_code)
workingdollhouse.dh_background = background
Because different POST arguments will refer to different fields, you will need to know what each one refers to and treat them differently. A simple “setattr for each one” won't work.

Related

use for loop with JavaScript template literal function

I have a web app, frontend using normal HTML5, backend using Django.
In the frontend page, I have a JavaScript template literal function. Which is supposed to render all the individual value into a selection box of a queryset passed from backend to a bootstrap table.
view.py:
def view_material(request):
query_results_publisher = Publisher.objects.all()
return render(request, 'material/index.html', context={'Publisher':query_results_publisher})
publisher is query set: <QuerySet [<Publisher: >, <Publisher: Arkib Negara Malaysia>, <Publisher: DBP>, <Publisher: Edward Elgar>...]
index.html(bootstrap table + javascript template literal function):
...
<th class ='publisher' data-field="book.publisher" data-formatter="renderPublisher">Publisher</th>...
<script>
var Publisher = "{{ Publisher }}";
var publisher = '';
function renderPublisher(value) {
return `select style="width: 7em" name="" id="">
for (publisher in ${Publisher}) {
<option value="eText" ${(value === 'eText') ? 'selected="selected"' : ""}>
publisher</option>}</select>}`
</script>
But my for loop in javascript template literal function is not working, seems like I have some problem with the template literal usage in for loop.
How to correct my function?
You need check value of Publisher before loop it, if it is array, use for-of, if is object, use for-in. But in your case, I saw you print publisher, not it property, so i guest it must be array ( change if im wrong ) . Something like :
var publishers = "{{ Publisher }}"; // no capital for variable' name
function renderPublisher(value) {
var renderString = 'select style="width: 7em" name="" id=""'
for (publisher of publishers) {
renderString += `
<option value="eText" ${value === 'eText' ? "selected" : ""}> ${publisher}</option>`
}
renderString += '</select>'
return renderString;
}

How to perform .delete() queryset in Django in a ListView?

Here is what I've done so far:
1.) I've made a javascript function that gets all the id's of the items (using checkbox select) in the database like so (this is DataTables):
function () {
// count check used for checking selected items.
var count = table.rows( { selected: true } ).count();
// Count check.
// Count must be greater than 0 to delete an item.
// if count <= 0, delete functionality won't continue.
if (count > 0) {
var data = table.rows( { selected: true } ).data();
var list = [];
for (var i=0; i < data.length ;i++){
// alert(data[i][2]);
list.push(data[i][2]);
}
var sData = list.join();
// alert(sData)
document.getElementById('delete_items_list').value = sData;
}
}
It outputs something like 1,2,5,7 depending on what rows I have selected.
2.) Passed the values inside a <input type="hidden">.
Now, I've read a post that says you can delete data in Django database using a checkbox, but I'm not sure how exactly can I use this.
I'm guessing I should put it in the ListView that I made, but how can I do that when I click the "Delete selected items" button, I can follow this answer?
I'm trying to achieve what Django Admin looks like when you delete items.
My ListView looks like this:
Yes you can use linked example. Django Admin do it the same way, You send selected ids and django do filtering by given values and after django apply selected action for selected items.
UPDATE
For example.
class List(ListView);
def post(self, request, *args, **kwargs):
ids = self.request.POST.get('ids', "")
# ids if string like "1,2,3,4"
ids = ids.split(",")
try:
# Check ids are valid numbers
ids = map(int, ids)
except ValueError as e:
return JsonResponse(status=400)
# delete items
self.model.objects.filter(id__in=ids).delete()
return JsonResponse({"status": "ok"}, status=204)
And html:
<button id="delete-button">Del</button>
<div id="items-table">
{% for object in objects_list %}
<div class="item" data-id="{{object.id}}">{{ object.name }}</div>
{% endfor %}
</div>
<script>
$(function(){
$('#delete-button').on('click', function(e) {
// Get selected items. You should update it according to your template structure.
var ids = $.map($('#items-table item'), function(item) {
return $(item).data('id')
}).join(',');
$.ajax({
type: 'POST',
url: window.location.href ,
data: {'ids': ids},
success: function (res) {
// Update page
window.location.href = window.location.href;
},
error: function () {
// Display message or something else
}
});
})
})();
</script>

Dynamically update Django form field options using Ajax to GET new queryset

I'm new to coding and django and I'm struggling to find the solution to the following problem having reviewed the answers I've found.
Im creating a search form with multiple fields. When the user selects the first field category (and before hitting search) I would like to dynamically change the queryset for the second field sub_category such that only related values are shown.
I have models.py as follows:
class Product(models.Model):
category = models.ForeignKey("Category")
sub_category = models.ForeignKey("SubCategory")
class Category(models.Model):
name = models.CharField(max_length=256)
class SubCategory(models.Model):
category = models.ForeignKey("Category")
name = models.CharField(max_length=256)
And my forms.py includes:
class BasicSearchForm(forms.Form):
category = forms.ModelChoiceField(
label='Category',
queryset=Category.objects.all(),
to_field_name="name",
empty_label=None,
initial="Red")
sub_category = forms.ModelMultipleChoiceField(
required=False,
label='Type',
queryset= SubCategory.objects.all(),
to_field_name="name",
widget=forms.Select)
And my views.py includes:
def search(request):
if request.method == 'POST':
form = BasicSearchForm(request.POST)
if form.is_valid():
category = form.cleaned_data['category']
sub_category = form.cleaned_data['sub_category']
return render(request, 'myapp/search.html', {'form': form})
else:
form = BasicSearchForm()
return render(request, 'myapp/search.html', {'form': form})
And finally the search.html includes:
<form class="search-form" role="search" action="/search/" method="get">
{{ form }}
<input type="submit" value="Search" />
</form>
I've played around with a few answers but nothing seems to work. I'd really appreciate some help. Thanks in advance!
Update:
Thanks for the feedback. As a result I updated the following:
In my urls.py:
urlpatterns = [
url(r'^ajax/update_subcategories/$', views.update_subcategories, name='update_subcategories'),
And in my views.py:
def update_subcategories(request):
category = request.GET.get('category', None)
sub_category = list(SubCategory.objects.filter(category__name__exact=category).values('name'))
return JsonResponse(sub_category, safe=False)
And I have this in my myapp/search.html:
{% block javascript %}
<script>
$("#id_category").change(function () {
var category = $(this).val();
$.ajax({
url: '{% url "myapp:update_subcategories" %}',
data: {
'category': category,
},
success: function (response) {
var new_options = response;
alert(new_options[0].name); // works
$('#id_sub_category').empty();
$.each(new_options, function(key, value) {
$('#id_sub_category')
.append($('<option>', { value : key })
.text(value.name));
});
}
});
</script>
{% endblock %}
Update: The sub_category options were showing as [object Object] until I changed value to value.name and it looks like it's working. I'll test it out and close unless there are any comments.
Update: Im still having an issue with the browser back button. When a user clicks back the dropdown values have changed back to the original queryset rather than the updated version.
You can't do this from Django views side, ie, backend. You could try an ajax request for implementing this kind of requests, by sending a GET request to the server for populating the drop-down or whatever you are into.
For a simple example, you could refer
here
How do I POST with jQuery/Ajax in Django?
EDIT
def update_subcategories(request):
category = request.GET.get('category', None)
sub_category = list(SubCategory.objects.filter(category__name__exact=category).values('name'))
return JsonResponse(dict(sub_category=sub_category))
Then in ajax response you could grab it like response.data.sub_category
Use ajax to send the category and retrieve subcategory elements.
For the category, send it via get request, and using the orm return the subcategories in a json format which you can show using jQuery.

Value in object returns to empty

I'm using a select form with data retrieved from a JSON url. When I select something, I want the value to be put into $scope.afspraak.groep. For some reason, the value returns to the original value which is empty.
Here is my code:
<select id="selectgroep" name="selectgroep" class="form-control" size='5' ng-model="afspraak.groep" ng-options="t.id as t.id for t in objecten" ng-click="test()">
</select>
$scope.afspraak = {
groep: '',
};
$scope.passData = function(data) {
$scope.afspraak.groep = data;
}
$scope.test = function() {
console.log($scope.afspraak);
}
I have used various methods such as changing ng-click to passData(afspraak.groep), but it still doesn't work. The weird thing is that in another partial, I have the exact similar code and that does work shown here:
<select id="selectvak" name="selectvak" class="form-control" size='5' ng-model="user.vakid" ng-options="t.id as t.id for t in vak" ng-click="getKlas(user.vakid); test()">
</select>
$scope.user = {
vakid: '',
};
$scope.test = function() {
console.log($scope.user);
}
$scope.getKlas = function (ID){
afsprakenService.getKlassen(ID)
.success(function (klas){
$scope.klas = klas;
$scope.alerts.push({ type: 'success', msg: 'Retrieved'});
})
.error(function (error) {
$scope.alerts.push({ type: 'danger', msg: 'Error retrieving! ' + error.message});
});
};
What am I doing wrong here? The only difference that I see is the method in the second select form which is getKlas where I pass the ng-model to use in another function.
Edit: this is now solved! It turns out that a label class is what was causing the deletion. I removed it by accident and it works now!
To set a models value from a select it is sufficient to have a ng-model on it. In case you want to do some extra action when selecting an option you should use ng-change and not ng-click.
A good way to debug your models, to check in real time if it was updated and view its values do something like this
<pre>{{ myModel | json }}</pre>
Here is a working fiddle example: http://jsfiddle.net/jub58sem/2/

tastypie and x-editable using patch

I almost have x-editable working with the django API built with tastypie thanks to various other answers on stackoverflow, but not quite.
Here is the html:
<a href="#" id="field_name" class="editable"
data-name="name"
data-pk="{{ object.id }}"
data-value="{{ object.name }}"
data-title="Meeting Name">
{{ object.name }}</a>
and the javascript:
$('.editable').on('init', function(e, edt) {
edt.options.url = '/api/v1/update_meeting/' +edt.options.pk;
});
$('.editable').editable({
mode: 'inline',
ajaxOptions: {
type: 'PATCH'
},
success: function(response, newValue) {
// nothing to do
}
});
And the tastypie resource:
class UpdateMeetingResource(ModelResource):
class Meta:
queryset = Meeting.objects.all()
resource_name = 'update_meeting'
limit = 0
include_resource_uri = False
list_allowed_methods = ['get','patch',]
detail_allowed_methods = ['get', 'patch']
serializer = urlencodeSerializer()
authentication = Authentication()
authorization = Authorization()
My only problem is that the field name gets updated with "name" and not the value in data-value. Before I put in the data-name attribute, it was setting the value to "field_name".
I could fix this by simply changing the patch-detail method in my tastypie resource, but it would be nice to get it working without doing that.
So the problem was that the patch being sent was a key value pair and that was not being recognised by tastypie, but a quick modifiation to hydrate fixes it. Maybe not the best solution but here is what is working for me:
The html:
<a href="#" id="name" class="editable editable_default_setup"
data-title="Name"
data-pk="{{ object.pk }}"
data-value="{{ object.name }}"
data-placeholder="Meeting Name" >
{{ object.name }}</a>
The javascript:
$('.editable').on('init', function(e, edt) {
edt.options.url = '/api/v1/meeting/' + {{ object.id }};
});
$.fn.editable.defaults.ajaxOptions = {type: "PATCH"};
$('.editable_default_setup').editable();
Setting up the resource:
class Meta:
queryset = Meeting.objects.all()
include_resource_uri = False
resource_name = 'meeting'
limit = 100
allowed_methods = ['post','put','get','options','patch', 'delete']
detail_allowed_methods = ['patch']
authentication = Authentication()
authorization = Authorization()
serializer = urlencodeSerializer()
always_return_data=True
The important bit, Modification to the tastypie resource:
def hydrate(self, bundle):
if bundle.request.method == "PATCH":
# data is supplied by x-editable in format {u'pk': u'1170', u'name': u'owner', u'value': u'5', u'format': u'json'}
# apply this value to the obj and return
field_name = bundle.data['name']
field_value = bundle.data['value']
# the use of name is unfortunate as it can override a field called name, so put it back to original value unless updating it
# do the same with value, just in case
bundle.data['name'] = getattr(bundle.obj, 'name', None)
bundle.data['value'] = getattr(bundle.obj, 'value', None)
# now set the attribute field_name to field_value so object will update
bundle.data[field_name] = field_value
setattr(bundle.obj, field_name, field_value)
return bundle

Categories

Resources