Django queryset list that have expanded relational model data - json

In my Django webapp, I have Function Based API that display a list for with pagination. The problem is I want each row in the list to have expanded output of relational field.
This is my model for attendance:
class Attendance(models.Model):
CHECKIN = 1
CHECKOUT = 2
ATTENDANCE_TYPE_CHOICES = (
(CHECKIN, "Check In"),
(CHECKOUT, "Check Out"),
)
employee = models.ForeignKey(Employee)
company = models.ForeignKey(Company)
activity_type = models.IntegerField(choices = ATTENDANCE_TYPE_CHOICES, default=CHECKIN)
This is part of my list API where I define what fields I want to display and is paginated.
employee_list = Employee.objects.filter(company = auth_employee.employee.company.id).values("id","employee","company","activity_type").order_by('id')
page = request.GET.get('page', request.POST['page'])
paginator = Paginator(employee_list, request.POST['page_limit'])
try:
employees = paginator.page(page)
except PageNotAnInteger:
employees = paginator.page(request.POST['page'])
except EmptyPage:
employees = paginator.page(paginator.num_pages)
return Response(list(employees), status=status.HTTP_200_OK)
The output is a list like this but without employee detail.
[
{
"id": 14,
"employee": 15,
"company": 15,
"activity_type": 1,
},
{
"id": 15,
"employee": 15,
"company": 15,
"activity_type": 2,
}
]
My expected output, how do I get something like this ?
[
{
"id": 14,
"employee": {
"id":"2",
"name":"Michael"
},
"company": 15,
"activity_type": 1,
},
{
"id": 15,
"employee": {
"id":"2",
"name":"Jeniffer"
},
"company": 15,
"activity_type": 2,
}
]
How can I produce a list that shows the relational data like above to display in the json response ?

What you're describing is serialization, in this case nested serialization in which django model relationship references get serialized in the structure you described above.
There's no out of the box way to do this with django. The django-rest-framework project has solved this issue with their nested serializers
If you're working on an API that will be outputting the results to JSON to be consumed by some service or client other than Django's templates, I would seriously consider adding that to your project.

Related

How to search for MongoDB documents by name using an API GET endpoint?

I currently have a list of businesses in a MongoDB database (the data structure can be found below). I would like to create a REST API endpoint to search for documents by "name" instead of "_id".
[
{
"_id": "1",
"name": "Apple",
"reviews": [
{
"_id": "1",
"comment": "Good",
}
]
},
{
"_id": "2",
"name": "Microsoft",
"reviews": [
{
"_id": "1",
"comment": "Great",
}
]
}
]
Currently, I have a GET API endpoint that utilises the find_one() method where the user can supply the business _id field in the URL and retrieve that business. For example, if the user visits http://localhost:5000/api/v1.0/businesses/1, Apple will be retrieved along with its reviews.
#app.route("/api/v1.0/businesses/<string:id>", methods=["GET"])
def show_one_business(id):
business = businesses.find_one({'_id':ObjectId(id)})
if business is not None:
business['_id'] = str(business['_id'])
for review in business['reviews']:
review['_id'] = str(review['_id'])
return make_response( jsonify ( [ business ] ), 200 )
else:
return make_response( jsonify( {"error" : "Invalid business ID"} ), 404 )
How do I create an API endpoint to retrieve Apple by providing its name? I assume it will be similar to the REST API above, but I attempted to change the '_id' values to 'name' values, but that didn't work.

mapping nested object as props to custom component

I have an app where the user will search for a term and they will see the results rendered. The results, in this case, are from a nested JSON object. I have a component called CompanyInfoList that passes props to Results component that renders the JSX. The props are employee, date, tax, and balance. I tried to map within a map in the component, but it did not work. My goal is to get access to the details data, how do I do this. The files to look at are CompanyInfoList and Results. The data is loaded in via axios in CompListContext
In the CompSearch comp when you enter "ABC" nothing will happen, because I am not accessing the details data from the JSON obj. This is what I need help in doing.
Here is the mongo DB JSON object (pasted from PostMan):
"data": {
"details": {
"employee": "person1",
"date": "test date",
"tax": "test tax",
"balance": "22"
},
"company": "TEST-ABC",
"_id": "60dba9fe7641a44d40364c1f",
"__v": 0
}
Here is my code
Given the company array object shape:
{
details: {
employee: "person1",
date: "test date",
tax: "test tax",
balance: "22"
},
company: "TEST-ABC",
_id: "60dba9fe7641a44d40364c1f",
__v: 0
}
You are filtering by the element's company property, and when mapping the filtered results in CompanyInfoList you need to access the details property, i.e. result.details.employee.
const CompanyInfoList = ({ filtered }) => {
const fltr = filtered.map((result) => (
<Results
key={result.details.id}
employee={result.details.employee}
date={result.details.date}
tax={result.details.tax}
balance={result.details.balance}
/>
));
return <>{fltr}</>;
};

typeahead nested json object

I am new to Ember and JSON. I want to parse a JSON object that is below with typeahead library
and access nested object values by searching their keys.
I have this Json format:
return [
{
"id": 1,
"category_name": "Supermarket",
"category_description": "SUPER MARKET",
"image_url": "",
"merchants": [
{
"name": "CARREFOUR",
"id": 12,
"merchant_type_id": 1,
"merchant_type_description": "Gold",
"merchant_redeption_rate": 0.002500,
"image_url": "https://jpg",
"branches": [
{
"id": 123456,
"latitude": 37.939483,
"area": "ΑΓ. ΔΗΜΗΤΡΙΟΣ",
"zip": "12345"
},
{
"id": 4567890,
"longitude": 23.650622,
"area": "ΑΓ. ΙΩΑΝΝΗΣ ΡΕΝΤΗΣ",
"zip": "12345"
}
]
},
{
"name": "CAFCO",
"id": 13,
"merchant_type_id": 3,
"merchant_type_description": "None",
"merchant_redeption_rate": 0.002500,
"image_url": "https:.jpg",
"branches": [
{
"id": 127890,
"latitude": 38.027870,
"area": "ΠΕΡΙΣΤΕΡΙ",
"zip": "12345"
}
]
}
]
},
{
"id": 2,
"category_name": "Πολυκαταστήματα",
"category_description": "ΠΟΛΥΚΑΤΑΣΤΗΜΑ",
"image_url": "",
"merchants": [
{
"name": "AGGELOPOYLOS CHR.",
"id": 15,
"merchant_type_id": 2,
"merchant_type_description": "Silver",
"merchant_redeption_rate": 0.002500,
"image_url": "https://www.nbg.gr/greek/retail/cards/reward-programmes/gonational/PublishingImages/aggelopoulos.jpg",
"branches": [
{
"id": 234780,
"latitude": 35.366118,
"longitude": 24.479461,
"address": "ΕΘΝ. ΜΑΚΑΡΙΟΥ 9 & ΕΛ. ΒΕΝΙΖΕΛΟΥ 1",
"area": "Ν. ΦΑΛΗΡΟ",
"zip": "12345"
}
]
}
]
}
];
--------------------------Updated----------------------------
For example, i want to search using typeahead the name of merchants and when the letter we write to search matches the name of merchants it will appear the corresponding category_name and backwards.
Example -> when i keyboard the s it will appear :
Category : Supermarket,
Name: CARREFOUR
Name: CAFCO
And the same output on the dropdown of search when i keyboard the letter c.
Any help?
New Jsbin example
The simplest way (in my mind) to get this to work is to create a computed property that will contain an array of latitudes. But how do we get there?
To get to latitude, you need to go through array of merchants and then array of branches. Being that this will be across multiple elements, you are going to end up with "array of arrays" type data structure, which is annoying to deal with. So, to simplify this, we can create a simple flatten function as follows:
flatten: function(origArray){
var newArr = [];
origArray.forEach(function(el) {
el.forEach(function(eachEl){
newArr.push(eachEl);
});
});
return newArr;
},
In addition to our function above, Ember already provides us with many other useful functions that can be used on arrays (see here). One of those is mapBy(property) which transforms an array into another array only keeping the values of the property we specified.
So, to create a lats (for latitudes) property, we can just do this:
lats: function(){
var merchantsArr = this.get('model').mapBy('merchants');
merchantsArr = this.flatten(merchantsArr);
var branchesArr = merchantsArr.mapBy('branches');
branchesArr = this.flatten(branchesArr);
return branchesArr.mapBy("latitude").compact();
}.property('model')
Above, I am basically using mapBy, flatten (see above) and compact which
Returns a copy of the array with all null and undefined elements removed.
Once you have the lats property with all the necessary data, the rest is easy.
Your call to component becomes:
{{x-typeahead data=lats name='category_name' selection=myColor}}
Note lats instead of model you originally were passing into the component.
And now, to access the value of data property in the component, you do
`this.get('data')`
which you can just pass in as the source like so:
source: substringMatcher(self.get('data'))
Working solution here
Update
Updating my answer based on your updated question.
OK, so this is getting a little more complicated. You now need more than just one property (latitude) from the object. You need category_name and merchant name.
In addition to mapBy, which just grabs one property out of array, Ember also has map which lets you transform the array into pretty much anything you want to:
lats: function(){
var merchantsArr = this.get('model').map(function(thing){
var category_name = thing.category_name;
return thing.merchants.map(function(merchant){
return {
"name": merchant.name,
"category": category_name
};
});
});
merchantsArr = this.flatten(merchantsArr);
return merchantsArr;
}.property('model')
The code above looks complicated, but it's basically just returning an array of top level objects' merchants accompanied by category_name. Since this is an array of arrays, we will need to flatten it.
Then, inside the component, we need to keep in mind that we are not just passing in an array of strings, but rather we are passing in an array of objects. Therefore, we need to look through object's properties (name and category) for a match
$.each(strs, function(i, str) {
if (substrRegex.test(str.name) || substrRegex.test(str.category)) {
matches.push(str);
}
});
Lastly, to actually display both category and merchant name, you need to tell Typeahead how to do that:
templates: {
suggestion: Handlebars.compile('<p>{{name}} – {{category}}</p>')
}
Working solution here

Use $resource of AngularJS to find specific element in json Array

I am new to AngularJS. Trying to get specific element from a JSON array via $resource.
The structure of JSON file staffs.json is like:
[{
"id": 0,
"facility_id": [0],
"name": "Tim",
"role_id": 0
},
{
"id": 1,
"facility_id": [0],
"name": "Duncan",
"role_id": 0
},
{
"id": 2,
"facility_id": [0],
"name": "Tony",
"role_id": 1
},
{
"id": 3,
"facility_id": [0],
"name": "Parker",
"role_id": 1
},
{
"id": 4,
"facility_id": [0],
"name": "Manu",
"role_id": 2
},
{
"id": 5,
"facility_id": [0],
"name": "Ginobili",
"role_id": 2
},
{
"id": 6,
"facility_id": [0],
"name": "Tiago",
"role_id": 3
},
{
"id": 7,
"facility_id": [0],
"name": "Splitter",
"role_id": 3
}]
I am trying to get a staff whose name is "Tiago".
The code is:
var url = 'data/staffs.json';
var username = 'Tiago';
users = $resource(url);
users.get({name: username}, function(data){
alert(data.name);
});
It seems the alert() function inside the get() never gets called. However if I changed the method from users.get() to users.query(), it can get the list of the staffs. I guess this is because the data inside the JSON file is an array, so the query() which is used to get array works, while the get() does not work because it is not for array operation. Am I correct?
I am just wondering if I have to use query() get the whole array and match the elements one by one until I find the one with the same name, or there are some simpler ways to get the element I want.
Thanks
AngularJS resource has a separate query function to avoid JSONP vulnerability for arrays. You have two options:
get all and find the element in the array on the client side
add extra API endpoint for single user and fetch it by the name
I vote for option two, since you don't have to send everything over the wire and you use server (DB) to get the specific user. Server software is optimised for that.
Your best bet would by fetching data with the query() method, then using indexOf()
var url = 'data/staffs.json';
var username = 'Tiago';
users = $resource(url);
users.get({name: username}, function(data){
dataOfMyUser = data.map(function(cur) { return cur.name }).indexOf(username);
alert(dataofMyUser);
});

emberjs JSON format handling

I have an Employee model in my emberjs app, and I am trying to load the Employees content using a RESTful web service which come in the following format:
{
"Result": [
{
"EmployeeId": "1",
"EmployeeName": "Mark Smith",
"Active": 0,
"Dept": "Sales"
},
{
"EmployeeId": "2",
"EmployeeName": "John Smith",
"Active": 1,
"Dept": "Sales"
},
{
"EmployeeId": "3",
"EmployeeName": "Michael Smith",
"Active": 1,
"Dept": "Administration"
}
],
"ResultCount": 3
}
Here I am facing 3 problems:
Is it possible to read this JSON format and add it to the Employee model, I understand that "Result" should have been "Employees" but I have no control over the return JSON format so if it is possible to use "Result" that will be great. Any example on doing so is highly appreciated.
How can I handle "ResultCount"? Is there a way I can read it as part of Employee model?
How I can read "Active" in the app View as "Active" / "Not Active" instead of 0 or 1?
Thanks for your time
As pointed out by jasonpgignac, you'll need to write a custom serailizer/deserializer to get the data into ember-data.
Once you have your data loaded ResultCount isn't required. You should use the 'length' property on the returned collection.
As part of your serializer you'll want to convert 0/1 to false/true in your model. You can them add a property like:
activeLabel: ( ->
if #get('active')
'Active'
else
'Not Active'
).property('active')
and use this property in your templates.
As requested, here's a sample class from a project of mine:
App.StudentSerializer = DS.ActiveModelSerializer.extend
serializeBelongsTo: (record, json, relationship) ->
key = relationship.key
if key is 'attendance'
#serializeAttendance(record, json)
else
json["#{key}_id"] = record.get("#{key}.id")
serializeAttendance: (record, json) ->
attendance = record.get('attendance')
json['attendance'] = {}
['monday', 'tuesday', 'wednesday', 'thursday', 'friday'].forEach( (day) =>
json['attendance'][day] = attendance.get(day)
)
serializeHasMany: (record, json, relationship) ->
key = relationship.key
jsonKey = Ember.String.singularize(key) + '_ids'
json[jsonKey] = []
record.get(key).forEach( (item) ->
json[jsonKey].push(item.get('id'))
)
My store.coffee looks like:
App.Store = DS.Store.extend
# Override the default adapter with the `DS.ActiveModelAdapter` which
# is built to work nicely with the ActiveModel::Serializers gem.
adapter: '_ams'
App.ApplicationSerializer = DS.ActiveModelSerializer.extend()
You'll probably want to use the JsonAdapter and extend it if you don't have any control over the backend. As I said below, I haven't done deserialising but there should be the necessary hooks for you to convert into the format required by ember-data.