How to Render Django ForeignKey relation on Datatables Serverside - python

Im stuck for days, im using this plugin django-datatables-view and I need to render ForeignKey related value in serverside datatables, it's like: {{data.pasien_id.name}} in typical django template. but that way not work with serverside datatables, and there is no documentation anywhere to achieve that. the code shown below.
Models.py
class Pasien(models.Model):
nama = models.CharField(max_length=40, null=True)
class Log_book(models.Model):
pasien = models.ForeignKey(Pasien, on_delete=models.PROTECT, null=True)
Views.py
class logbook_json(BaseDatatableView):
model = Log_book
columns = ['id', 'pasien_id']
order_columns = ['id','pasien_id']
def render_column(self, row, column):
if column == 'id':
return escape('{0}'.format(row.id))
else:
return super(logbook_json, self).render_column(row, column)
def filter_queryset(self, qs):
filter_customer = self.request.GET.get('search[value]', None)
if filter_customer:
customer_parts = filter_customer.split(' ')
qs_params = None
for part in customer_parts:
q = Q(id__icontains=part) | Q(pasien_id__icontains=part)
qs_params = qs_params | q if qs_params else q
qs = qs.filter(qs_params)
return qs
templates.html datatables load,
<script class="init" type="text/javascript">
$(document).ready(function () {
$('#responsive-datatablex').DataTable({
// ...
searching: true,
processing: true,
serverSide: true,
stateSave: true,
"ajax": "{% url 'logbook_json' %}",
});
});
</script>

It's easier if you get your JSON to return in a flat structure (rather than nested):
{
"name": name,
"id": id
}
Then you need to define data for the column to read values from the JSON. This can be quite tricky so it pays to read the documentation carefully.
You must define your datatables column correctly so that it can read this data. Note that there are "orthogonal" data types (ie. for sorting, display etc):
columns: [
{
"title": "Name",
"data": "name",
"render": {
"_": "name",
},
"searchable": true,
"orderable": true,
"defaultContent": "N/A"
}
]
There are other ways of achieving the same, so refer to the docs. The key thing is that the DataTables column definition must match what is returned in the JSON.
Some tips:
Try to get one column working by itself first.
Load the JSON in a separate tab so that you can see that it is being rendered correctly. Note that there are some specific rules about rendering the serverside response, I expect that django-datatables-view honours these but I haven't checked.

Related

Flask SQLAlchemy Marshmallow | How do I query all entries between two ID values

I want to be able to query a database and jsonify() to results to send over the server.
My function is supposed to incrementally send x amount of posts every time it called, i.e. Sending posts 1 - 10, ..., Sending posts 31 - 40, ...
I have the following query:
q = Post.query.filter(Post.column.between(x, x + 10))
result = posts_schema.dump(q)
return make_response(jsonify(result), 200) // or would it be ...jsonify(result.data), 200)?
Ideally, it would return something like this:
[
{
"id": 1,
"title": "Title",
"description": "A descriptive description."
},
{
"id": 2,
...
},
...
]
The SQLAlchemy model I am using and the Marshmallow schema:
class Post(db.Model):
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(30))
content = db.Column(db.String(150))
def __init__(self, title, description):
self.title = title
self.description = description
class PostSchema(ma.Schema):
class Meta:
fields = ('id', 'title', 'description')
posts_schema = PostSchema(many=True)
I am new to SQLAlchemy, so I don't know too much about query yet. Another user had to point me in the direction I am in now with the current query, but I don't think it is quite right.
In SQL, I am looking to reproduce the following:
SELECT * FROM Post WHERE id BETWEEN value1 AND value2
To paginate with SQL Alchemy you would do the following:
# In the view function, collect the page and per page values
#app.route('/posts/<int:page>/<int:per_page>', methods=['GET'])
def posts(page=1, per_page=30):
#... insert other logic here
posts = Post.query.order_by(Post.id.asc()) # don't forget to order these by ID
posts = posts.paginate(page=page, per_page=per_page)
return jsonify({
'page': page,
'per_page': per_page,
'has_next': posts.has_next,
'has_prev': posts.has_prev,
'page_list': [iter_page if iter_page else '...' for iter_page in posts.iter_pages()],
'posts': [{
'id': p.id,
'title': p.title,
'content': p.content
} for p in posts.items]
})
On the front end, you would use the page_list, page, per_page, has_next, and has_prev values to help the user choose which page to go to next.
The values you pass in the URL will dictate which page to go to next. This is all handily built into SQLAlchemy for you, which is another reason it is such a great library.
I found out a solution to my question:
Post.query.filter((Post.id >= x) & (Post.id <= (x + 10))).all()

How to return the partial result of a search to a grid?

Currently, the system I work on (python 2.7 / django 1.6 / dojo toolkit) has a textual search feature inside .pdf files. It's a heavy search and the return is total: it returns all the records that matched the search only when it finishes browsing all the records.
Exemple.:
views.py
def search_document(request):
"""check permission and logic to show the grid """
varis = {
'url2': request.app_root + 'mod/document',
'url': request.app_root + 'mod/search-document-json',
'query': query
}
return render_to_response('mod/search_document_grid.html', RequestContext(request, varis))
def search_document_json(request, query=''):
# Sort
# srt logic (no problem here)
# Pagination
# pagination logic (no problem here)
# Searching for docs in database
# query database logic (no problem here)
documents = Document.objects.filter(QQ).order_by(srt).iterator()
list_docs = []
for doc in documents:
if doc.file.endswith('.pdf'):
# PDFMiner stuff (no problem here)
p = re.compile(query, re.IGNORECASE)
m = p.search(text)
if m:
list_docs.append(doc)
qtr = len(list_docs)
varis = {'lista': lista[ini:fim]}
templ = get_template('mod/search_document.json')
resp = HttpResponse(templ.render(RequestContext(request, varis)), content_type="application/json")
resp['Content-Range'] = rg
return resp
urls.py
url(r'^search-document/$', search_document, name='search_document'),
url(r'^search-document-json/(?P<query>.*)$', search_document_json, name='search_document_json'),
search_document_grid.html
require([
"dojo/cookie",
"dojo/aspect",
"dojo/store/JsonRest",
"dojo/store/Memory",
"dojo/store/Cache",
"dojox/grid/EnhancedGrid",
"dojox/grid/enhanced/plugins/Cookie",
"dojo/data/ObjectStore",
"dojo/_base/xhr",
"dojo/query",
"dojo/domReady!"
], function(cookie, aspect, JsonRest, Memory, Cache, EnhancedGrid, Cookie, ObjectStore, xhr, query){
var myStore = Cache(JsonRest({target:"{{url}}/{{query|urlencode}}"}), Memory());
var dataStore = new ObjectStore({ objectStore: myStore });
var grid = new dojox.grid.EnhancedGrid({
{% block gridoptions %}
store: dataStore,
rowsPerPage: 100,
keepRows: 200,
structure: {
defaultCell: { cellClasses: "normal" },
cells: [
{% block cells %}
{ name: "Name", field: "name", width: "300px"},
{ name: "Code", field: "code", width: "100px"},
{ name: "Version", field: "version", width: "60px"},
{ name: "Date", field: "date_pub", width: "80px"},
{ name: "Description", field: "description", width: "400px"},
"100px"}
{% endblock cells %}
]
},
{% endblock %}
}, "grid_1");
grid.startup();
I would like to return to the grid as the records match. What should be the logic to be used? Tool? Ajax? Jquery? Websockets(*edit2)?!?
edit:
At the moment I found a document that fits my query I want that the document go to the grid as the query still working
edit2:
As I still don't get what to do I'm thinking about change the entire project. From the day I asked this I still searching and found the new version of Django (3.1) that supports async methods, websockets, etc. This is the correct way? Anyone can help?

Django & Mongoengine get data from embedded document?

I am having trouble retrieving data from the embedded document in mongoengine.
models.py
from mongoengine import Document, EmbeddedDocument, fields
class ProductFields(EmbeddedDocument):
key_name = fields.StringField(required=True)
value = fields.DynamicField(required=True)
class Product(Document):
name = fields.StringField(required=True)
description = fields.StringField(required=True, null=True)
fields = fields.ListField(fields.EmbeddedDocumentField(ProductFields))
views.py
class ProductListView(APIView):
def get(self, request):
# list_products = Product.objects.all()
result=[]
productfields = ProductFields
for product in Product.objects:
data={
"name":product.name,
"description":product.description,
# "key":product.fields.key_name,
# "value":ProductFields.value,
}
print (data)
# print(productfields.key_name)
result.append(data)
return Response({"products":result,"message":"list of products.","requestStatus":1},status=status.HTTP_200_OK)
Output:
{
"description": "test description",
"name": "product1"
"fields":[
{ "key_name" : value},
{ "key_name" : value},
]
}
How do I get the above-desired output? Print function doesn't work because mongoengine returns object and not the value.
Here I see you're using APIView from Django Rest FrameWork. Have a look at django-rest-framework-mongoengine. If you're already familiar with DRF, you can use this extension to create your API endpoints with MongoDB easily.
You must have found some workaround by now even though you can mark this answer as correct so that if anyone else runs into the same problem in future, they can get the solution.

WTForm: FieldList with SelectField, how do I render?

I have this order form that allows my users to create an order. An order consists of multiple tuples of (producetype, quantity). Producetype should be rendered in a <select> form while quantity can just be an input. The choices of producetype should be dynamically added because that could change. Currently, I've written this in bare html
I would like to use WTForm for this because WTForm really simplifies my code. However, I am unable to do so:
Code:
class OrderEntryForm(Form):
quantity = IntegerField('Quantity',
[validators.Required(), validators.NumberRange(min=1)])
# we will be dynamically adding choices
producetype = SelectField('Produce',
[validators.Required()],
choices=[])
class OrderForm(Form):
name = TextField('Crop', [validators.Length(min=3, max=60)])
due_date = DateField('Due Date', [validators.required()])
order_entries = FieldList(FormField(OrderEntryForm))
I have the following questions:
How can I dynamically add choices to the order_entries field of the OrderForm?
If I have an order, order = { name:hello, due_date:2014-06-18, order_entries:[ {producetype_id: 1, quantity: 2}, {producetype_id: 3, quantity: 4}] }, how can populate my OrderForm with the right OrderEntryForm values?
How can I dynamically add choices to the order_entries field of the OrderForm
This depends on what you mean
If you mean you want to add lines items to the form after render. You have to use DOM manipulation to add these on the client side. WTForms has a naming convention for addressing indices on form fields. Its just name="<form-field-name>-<index>". If you add a name using javascript that follows this convention WTForms will know how to handle it on the backend.
If you mean you want to have a dynamic choice list then you can just iterate over the FieldList in the form after its instantiated. You can assign choices any iterable of 2-tuples. In the example below I am assigning them directly but they could just as easily have been retrieved from storage.
order_form = OrderForm()
for sub_form in order_form.order_entries:
sub_form.producetype.choices = [('2', 'apples'), ('2', 'oranges')]
how can populate my OrderForm with the right OrderEntryForm values?
You can just bind objects directly to the form using the obj keyword argument. WTForms is smart enough to dynamically build the form from the object's attributes.
from wtforms import Form, IntegerField, SelectField, TextField, FieldList, FormField
from wtforms import validators
from collections import namedtuple
OrderEntry = namedtuple('OrderEntry', ['quantity', 'producetype'])
Order = namedtuple('Order', ['name', 'order_entries'])
class OrderEntryForm(Form):
quantity = IntegerField('Quantity',
[validators.Required(), validators.NumberRange(min=1)])
# we will be dynamically adding choices
producetype = SelectField('Produce',
[validators.Required()],
choices=[
(1, 'carrots'),
(2, 'turnips'),
])
class OrderForm(Form):
name = TextField('Crop', [validators.Length(min=3, max=60)])
order_entries = FieldList(FormField(OrderEntryForm))
# Test Print of just the OrderEntryForm
o_form = OrderEntryForm()
print o_form.producetype()
# Create a test order
order_entry_1 = OrderEntry(4, 1)
order_entry_2 = OrderEntry(2, 2)
order = Order('My First Order', [order_entry_1, order_entry_2])
order_form = OrderForm(obj=order)
print order_form.name
print order_form.order_entries
The above example creates a sample Order and supplies it to the obj keyword. On render this will generate the following(unstyled):
For anyone else stumped by SelectFields + FieldLists, this is how I implemented a FieldList with SelectFields (inspired by nsfyn55's answer). This approach dynamically renders an arbitrary number of SelectFields to the /home route based on JSON data.
The route (routes.py):
#app.route('/home', methods=['POST', 'GET'])
def home():
custom_metadata = data
select_metadata_form_list = SelectFormList()
select_metadata_form_list.select_entries = get_select_entries()
context = {
"select_metadata_form_list": select_metadata_form_list,
}
return render_template('home.html', **context)
The forms (forms.py):
class SelectForm(FlaskForm):
select = SelectField("Placeholder", choices=[])
class SelectFormList(FlaskForm):
select_entries = FieldList(FormField(SelectForm))
The template (home.html):
{% for select_form in select_metadata_form_list.select_entries %}
{{select_form.select.label}}: {{ select_form.select}}
{% endfor %}
Helper methods (app.py):
def get_select_entries():
"""
Converts custom metadata to a forms.SelectForm(), which can then be
used by SelectFormlist() to dynamically render select items.
:return: <forms.SelectForm object>
"""
select_data = get_select_data_from_custom_metadata()
select_data_labeled = get_labled_select_data(select_data=select_data)
all_select_items = []
for select_dict in select_data_labeled:
for k, v in select_dict.items():
select_id = uuid.uuid1() # allows for multiple selects
select_entry = SelectForm()
select_entry.select.label = k
select_entry.id = select_id
select_entry.select.choices = v
all_select_items.append(select_entry)
return all_select_items
def get_select_data_from_custom_metadata():
"""
[
{"Seniority": ["Intern", "Associate", "Senior"]}
]
:return: List of dictionaries containing key and list of select values
"""
type = "select"
select_data = []
custom_metadata = get_custom_metadata()
for field in custom_metadata["fields"]:
if field["type"] == type:
select_data.append({field["key"]: field["data_list"]})
return select_data
The 'data' (custom_metadata.json):
{
"fields": [
{
"type": "text",
"key": "Position"
},
{
"type": "select",
"key": "Seniority",
"data_list": ["Intern", "Associate", "Senior", "Executive"]
},
{
"type": "select",
"key": "Company",
"data_list": ["Ford", "Chevy", "Toyota"]
},
{
"type": "select",
"key": "Team",
"data_list": ["Bucks", "Raptors", "Thunder"]
}
]
}
The result:
Add a SubmitField to your to OrderForm:
submit_something = SubmitField((u'Add something'))
and then call it from your view, and use the append_entry method of FieldList:
if form.submit_something.data:
form.order_entries.append_entry()
return render_template('yourtemplate.html', form=form)
Hope that helps !

How to create a filtered dropdown using Django forms?

I've got this model :
class QuestionInstance(models.Model):
questionsSet = models.ForeignKey(QuestionsSet)
question = models.ForeignKey(Question)
parent = models.ForeignKey('self',null=True,blank=True)
optional = models.BooleanField(default=False)
I'd like to create a dropdown, which user could choose one QuestionInstance.
It has to be filtered with questionsSet.
I've tested using a modelform like this, but it's not working :
(based on this How do I filter values in a Django form using ModelForm?)
class FormQuestionSelect(ModelForm):
instanceList = forms.ChoiceField(choices=[(questionInstance.id, questionInstance.question) for questionInstance in QuestionInstance.objects.all()])
class Meta:
model = QuestionInstance
fields = ('instanceList', )
widgets = {
'instanceList': Select(attrs={'class': 'select'}),
}
def __init__(self, questionsSet=None, **kwargs):
super(FormQuestionSelect, self).__init__(**kwargs)
if questionsSet:
#Tested many code here to filter, None of them worked :(
#Is that possible to create instanceList there ?
I'm not sure using a modelform is a good idea for this kind of purpose.
A modelform is great when create or update a model instance.
When using specific forms, like in this case, I'm using a custom form in template :
View
questionInstanceList = QuestionInstance.objects.filter(questionsSet=questionsSet)
Template
<select name="questionInstanceSelect">
{% for instance in questionInstanceList %}
<option value="{{ instance.id }}">{{ instance.question.text }}</option>
{% endfor %}
</select>
and process them this way :
instanceList = request.POST.get('questionInstanceSelect')
I'm quite sure there's a proper way.
You can change queryset of ModelChoiceField after form instantiation either in form __init__ or in view. but this wouldn't solve issue on client side. When someone change QuestionSet Question selectbox will remain the same
To update queryset just update form field's one
form.fields['parent'].queryset = (QuestionInstance.objects
.filter(questionsSet=questionsSet))
or if You change form __init__
self.fields['parent'].queryset = (QuestionInstance.objects
.filter(questionsSet=questionsSet))
But One should remember that if questionsSet is changed on client side parent list will remain the same.
Would You consider to add client side code updating parent's choice list
Let me explain a bit.
You have model
class QuestionInstance(models.Model):
questionsSet = models.ForeignKey(QuestionsSet)
question = models.ForeignKey(Question)
parent = models.ForeignKey('self',null=True,blank=True)
optional = models.BooleanField(default=False)
Here parent field link to self(the same model).
Let us use `Model form for this model
class FormQuestionSelect(ModelForm):
class Meta:
model = QuestionInstance
ModelForm will create fields for each model field with the same name
then after Its creation we update ModelChoiceField (created for ForeignKey) queryset
If you want your field to be dynamic, you need to use jQuery and ajax for this functionality. I have given code for use in django admin. You can tweak it a bit, if you want to use it in custom pages. But the concept remains same for both.
question_set_change.js
(function($){
$(function(){
$(document).ready(function() {
$('#id_questionsSet').bind('change', question_set_change);
$('#id_question > option').show();
if ($('#id_questionsSet').val() != '') {
var question_set_id = $('#id_questionsSet').val();
$.ajax({
"type" : "GET",
"url" : "/product_change/?question_set_id="+question_set_id,
"dataType" : "json",
"cache" : false,
"success" : function(json) {
$('#id_question >option').remove();
for(var j = 0; j < json.length; j++){
$('#id_question').append($('<option></option>').val(json[j][0]).html(json[j][1]));
}
}
});
}
});
});
})(django.jQuery);
// based on the questionsSet, questions will be loaded
var $ = django.jQuery.noConflict();
function question_set_change()
{
var question_set_id = $('#id_questionsSet').val();
$.ajax({
"type" : "GET",
"url" : "/product_change/?question_set_id="+question_set_id,
"dataType" : "json",
"cache" : false,
"success" : function(json) {
$('#id_question > option').remove();
for(var j = 0; j < json.length; j++){
$('#id_question').append($('<option></option>').val(json[j][0]).html(json[j][1]));
}
}
})(jQuery);
}
Include the following in views.py:
import simplejson
from django.shortcuts import HttpResponse
from app.models import Question
def question_choices(request):
question_list = []
question_set_id = request.GET.get('question_set_id')
questions = Question.objects.filter(question_set = question_set_id)
[question_list.append((each_question.pk,each_question.name)) for each_question in questions]
json = simplejson.dumps(question_list)
return HttpResponse(json, mimetype='application/javascript')
In urls.py:
from app.views import question_choices
urlpatterns = patterns(
(r'^question_set_change/', question_choices),
)
In admin.py where you want to load question based on question_set:
class Media:
js = ['/path/to/question_set_change.js',]

Categories

Resources