I am using caching in Django. My cache is a django-redis cache:
CACHES = {
"default": {
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": "redis://127.0.0.1:6379",
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
}
}
}
I am using view based cache:
#cache_page(timeout=60 * 10)
def my_view(request: Request):
# my view stuff
What I would like to know is what is the algorithm used by Django to create the key? The docs just say that it's made from the URL and headers. But I would like to know the specifics or better yet the code that generates it. But the docs lack this information.
So, how does Django derive keys for view based caches?
Found it!
https://docs.djangoproject.com/en/2.2/_modules/django/utils/cache/
What you are looking for is the method _generate_cache_header_key. Below is how it looks like as of writing this answer:
def _generate_cache_header_key(key_prefix, request):
"""Return a cache key for the header cache."""
url = hashlib.md5(force_bytes(iri_to_uri(request.build_absolute_uri())))
cache_key = 'views.decorators.cache.cache_header.%s.%s' % (
key_prefix, url.hexdigest())
return _i18n_cache_key_suffix(request, cache_key)
Related
I try to return a list of select options for countries using django-countries and django rest framework. I use JWT_AUTH for the authentication.
When I try a options request:
curl \
-H "Authentication: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFsYmVydG9fdmVudGEiLCJ1c2VyX2lkIjoyLCJlbWFpbCI6IiIsImV4cCI6MTUwODE2Mzg4Mn0.svxqTThCahSl1Vu27sMjuJyd1PRLk28-Xgn2OKKb5-g"\
-X OPTIONS \
-v http://127.0.0.1:8000/api/v1/core/perfilViajeroUserPass/
The response is:
{
"name":"Perfil Viajero User Pass Create",
"description":"",
"renders":["application/json","text/html"],
"parses":[
"application/json",
"application/x-www-form-urlencoded",
"multipart/form-data"
]
}
But I think that it should be something like this by default:
{
"name": "To Do List",
"description": "List existing 'To Do' items, or create a new item.",
"renders": [
"application/json",
"text/html"
],
"parses": [
"application/json",
"application/x-www-form-urlencoded",
"multipart/form-data"
],
"actions": {
"POST": {
"note": {
"type": "string",
"required": false,
"read_only": false,
"label": "title",
"max_length": 100
}
}
}
}
Someone could help me? thanks.
If you want to change some of the content:
name is the view's get_view_name which is the view's name slightly reworked.
description is the view's get_view_description which reworks the view's docstring.
Otherwise if you want something more complex, you'll probably want to customize the view's metadata as explained in http://www.django-rest-framework.org/api-guide/metadata/#custom-metadata-classes
I have found the solution.
I change my view class type from APIView to generics.CreateAPIView and know it works. Thank you very much.
Adding another answer since I recently ran into the same issue and found it a bit mystifying -- when making an OPTIONS request, Django Rest Framework uses the view's Metadata class to construct a response. The default Metadata class is SimpleMetadata, as mentioned in the docs. However, SimpleMetadata only adds the actions key to the response body if the view in question defines the method get_serializer(). I'm not sure why this is the case, but see here for the relevant code.
rest_framework.generics.GenericAPIView defines a get_serializer() method, so (authenticated) OPTIONS requests made to these views will return a response body with the actions key. But rest_framework.views.APIView does not define this method, so the actions key will always be absent.
If you have to use rest_framework.views.APIView, you could work around this by defining a get_serializer() method on your APIView class. Which feels a little hacky, but I tested it and it works:
class MyView(views.APIView):
def get_serializer(self):
return MySerializer()
def post(self):
...
I am using angular2 as a front end in my html pages.I have a django project that uses postgresql.
Which is the best approach to use the angular2 in the django project to connect to the django models and the database to perform basic operations(CRUD)like Read,Update etc?
Currently I need to fetch the data from the database dynamically.
(e.g.If user clicks on the product from the product list then product details should be retrieved from the database and it is shown to the user)
Any advice or reference example link will be helpful.
Create REST api end points using Django (use DRF for standard REST api's or just use vanilla django to generate json response for the requests and call it REST api).
For ex:
/product/:id is the api end point you've created to fetch the details of a particular product in Django
Then use Angular to request throught those API's and get the responses and do whatever you want with that data.
For ex:
make a get request to /product/1 to fetch the details of a product with PK = 1 when the user clicks that product.
Browse through Github for some inspiration.
Checkout django-rest-framework
DRF is a django app that makes building ReST apps a breeze.
Checkout their quick tutorial to get a sense of how to use DRF in your project.
I'm recently working on the similar project you have. My approach is just like #praba230890 mentioned above.
Here are some samples...
Django
In views.py
class HView(APIView):
def get(self, request, format=None):
request_hero_id = request.query_params.get('id', None)
if request_hero_id:
return Response(
{
'id': 1,
'name': 'test',
'position': 'mid'
},
status=status.HTTP_200_OK
)
return Response(
{ 'error': 'does not exist' },
status=status.HTTP_404_NOT_FOUND
)
class HsView(APIView):
def get(self, request, format=None):
return Response(
[
{
'id': 1,
'name': 'test',
'position': 'mid'
},
{
'id': 2,
'name': 'test',
'position': 'mid'
}
],
status=status.HTTP_200_OK
)
In urls.py
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'', include('shweb.urls')),
]
You will need to install django-cros-headers if you run into CROS errors. Also, you will need to configure your settings.py
Angular2
In api-service.service.ts
import { Injectable } from '#angular/core';
import { Http, Headers } from '#angular/http';
import 'rxjs/add/operator/toPromise';
import { Hero } from '../utils/hero';
#Injectable()
export class ApiService {
/** Modify this later */
private backend_api_url: string = 'http://localhost:8000/api/';
private api_headers: Headers = new Headers(
{ 'Content-Type': 'application/json' }
);
constructor(private http: Http) { }
getHero(id: number): Promise<Hero>{
const url = `${this.backend_api_url}htest/?id=${id}`;
return this.http.get(url).toPromise().then(
response => response.json() as Hero
).catch(this.handleError);
} // END getHero
getHeroes(): Promise<Hero[]>{
const url = `${this.backend_api_url}htests`;
console.log(url);
return this.http.get(url).toPromise().then(
response => {
console.log(response.json());
return response.json() as Hero[];
}
).catch(this.handleError);
} // END getHeroes
private handleError(error: any): Promise<any>{
console.error('An error occurred', error); // TODO: update this
return Promise.reject(error.message || error);
}
}
In hero.ts
export class Hero{
id: number;
name: string;
position: string;
}
In your component file, inject the api-service into component
#Component({
selector: 'app-dashboard',
templateUrl: './dashboard.component.html',
styleUrls: ['./dashboard.component.css']
})
export class DashboardComponent implements OnInit{
title: string = 'Dashboard';
heroes: Hero[] = [];
constructor(private apiService: ApiService){}
ngOnInit(): void{
this.getHeroes();
}
getHeroes(): void{
this.apiService.getHeroes().then(heroes => this.heroes = heroes);
} // END getHeroes
}
Basically, using API to retrieve data and cast into class; then, you can use those data.
PS. I haven't touched the credentials and security part. I believe you need to have some sort of Authentication implemented for secure API communication. (In my case, only GETs are allowed. Therefore, I put the security part for later.)
Hope this would help!
I tried to disable concurrency-control on Eve and trying to add another new id_field: "new_field", but I am not able to make it work. I look through various post in StackOverflow but still I am not able to fix it. Could someone please help?
I disabled : IF_MATCH = False in the global config and:
schema = {
"node_name": {
"type": "string",
"unique": True,
"required": True,
"node_type": {
"type": "string",
"required": True,
}
}
Config:
config = {
'item_title': 'new_title',
'additional_lookup': {
'url': 'regex("[\w]+")',
'field': 'node_name',
},
'id_field': "node_name",
'schema': schema,
}
And here is the url i am trying to send PATCH request:
url: http:localhost:5000/api/v1/resource/end_point/
Here
resource: my resource name
end_point: id_field value.
Could someone please help.
Have you enabled PATCH for your resource? By default, document and collection endpoints are read-only. Try adding the following to your configuration:
ITEM_METHODS = ['GET', 'PATCH', 'DELETE', 'PUT']
Also, you don't want to set an additional_lookup on the same field serving as id_field, as additional lookups are read-only.
Since you are not using a ObjectID as your unique key, you also probably need to change the default URL for the document endpoint. Try setting ITEM_URL to the correct regex:
ITEM_URL: URL rule used to construct default item endpoint URLs. Can be overridden by resource settings. Defaults regex("[a-f0-9]{24}") which is MongoDB standard Object_Id format.
Is it possible to call Django Manager Methods from AngularJS?
For example like this factory PUT method called in AngularJS:
var app = angular.module('app', ['ngResource', 'ngRoute']);
// Some APIs expect a PUT request in the format URL/object/ID
// Here we are creating an 'update' method
app.factory('Notes', ['$resource', function($resource) {
return $resource('/notes/:id', null,
{
'update': { method:'PUT' }
});
}]);
// snipped ...
Notes.update({ id:$id }, note);
But for Django's get_or_create for example?
So instead of this in Django:
obj, created = MyObject.objects.get_or_create(**kwargs)
I'd like to do something like:
app.factory('Notes', ['$resource', function($resource) {
return $resource('/notes/:id', null,
{
'get_or_create': // some code here but do not know what??
});
}]);
// snipped ...
Notes.get_or_update({ id:$id }, note);
I'd like to do this because I have written a good deal of custom Django Managers and would like to use AngularJS for the UI, but don't want to re-write them all if necessary.
Thanks
The point is that Angular is a client-side framework, and Django is server side. The only way they can communicate is via HTTP, which means that if you want Angular to call a Django manager method, you need to expose that via a URL connected to a view. As the comments say, one way to do that is via Django REST framework, but if that's overkill for your use case you can always write the views yourself.
I am running into a problem trying to include tastypie resources in a larger json response in a regular django view. I would like to have the view return something like this (based on a queryset generated in the view, not from typical tastypie get params):
{
"success": bool,
"message": string,
"error": string,
"objects": [
{
"field_one": bar,
"field_two": foo
}
... more objects ...
]
}
where objects list is a list of serialized tastypie resources, and success, message and error are coming from somewhere else in the view.
Right now, I can't figure out how to avoid turing the serialized resource into strings before the larger dict gets build, so I have something like this currently:
{
"success": bool,
"message": string,
"error": string,
"objects": [
"{"field_one": bar, "field_two": foo..."}",
"{"field_one": baz, "field_two": foobar..."}",
...
]
}
The whole point of this is to keep the model json representations consistent, to minimize friction between using the tastypie api directly, and using the data returned in these views. I'm thinking the solution is to somehow use the full_dehydrate method on each resource without serializing them, and then adding them to the bigger dict, and then serializing that dict, but I'm not sure what serializer to use. Or, maybe there is a better way.
As is often the case, writing this up helped me find a temporary solution. Maybe someone will have some input on how to make this better.
I am using this to prepare a queryset for serialization:
def serialize_queryset(resource_class, queryset):
# hand me a queryset, i give you dehydrated resources
resource = resource_class()
dd = {}
# make meta
dd['meta'] = {}
dd['meta']['limit'] = 1000
dd['meta']['next'] = None
dd['meta']['offset'] = 0
dd['meta']['previous'] = None
dd['meta']['total_count'] = len(queryset)
# objects
dd['objects'] = []
for obj in queryset:
bundle = resource.build_bundle(obj=obj)
dehydrated_obj = resource.full_dehydrate(bundle)
dd['objects'].append(dehydrated_obj)
# return dict
return dd
And I use the Serializer from tastypie.serializer. and in using it in a sample view is goes something like:
from tastypie.serializer import Serializer
serializer = Serializer()
def my_view(request):
#... do some other view stuff ...
# prepare a queryset for serialization
queryset = MyModel.objects.filter(is_cool=True)
data = serialize_queryset(MyModel, queryset)
# add in custom stuff, determined earlier in the view somewhere
data['success'] = success
data['message'] = message
data['error'] = error
# serialize and return response
data = serializer.serialize(data)
return HttpResponse(data, mimetype='application/json')
This seems to work. Maybe you see something bad about this method, or a way to improve it?