How to call class method in Django URLs path?
I want to call data_display method inside DataLoad class. how can i call it in my URLs.py file? so when i hit the path then it will render to the data_display.html template.
views.py
class DataLoad:
def __init__(self, save_path, name_of_file):
self.save_path = save_path
self.name_of_file = name_of_file
def file_load(self):
file_path = os.path.join(self.save_path, self.name_of_file+".html")
return file_path
def data_display(request,*args, **kwargs):
df = pd.read_csv("/home/satyajit/Desktop/opensource/data/us_amz.csv", low_memory=False)
json_records = df.reset_index().to_json(orient ='records')
data = []
data = json.loads(json_records)
context = {'data': data}
return render(request, "home/data_display.html", context)
urls.py
from apps.home.views import DataLoad
data = DataLoad.data_display(request)
urlpatterns = [
#path('data_display', DataLoad.as_view(), name='data_display'),
path('data_display', data, name='data_display'),
]
According to the documentation, your view must extends the View class and your method should be name as the HTTP method you want to match.
from django.views import View
class DataLoad(View):
def get(request, **kwargs):
df = pd.read_csv("/home/satyajit/Desktop/opensource/data/us_amz.csv", low_memory=False)
# ...
return render(request, "home/data_display.html", context)
# url.py
from apps.home.views import DataLoad
urlpatterns = [
path('data_display', DataLoad.as_view(), name='data_display'),
]
Dependency injection
This part answer the OP comment.
If you want to have a better decoupling of you DataLoad from the request, you will need to split your view from your Dataload classes.
This is called dependency injection.
So you have your DataLoad classes that don't use any HTTP related things:
# file: core/dataload.py
class DataLoad:
def __init__(self, save_path, name_of_file):
self.save_path = save_path
self.name_of_file = name_of_file
def file_load(self):
file_path = os.path.join(self.save_path, self.name_of_file+".html")
return file_path
def data_display():
df = pd.read_csv("...us_amz.csv", low_memory=False)
json_records = df.reset_index().to_json(orient ='records')
return json.loads(json_records)
You then build a view that will use this class.
Your View depends on DataLoad.
You inject a DataLoad instance into your view.
Your DataLoad class is decoupled from your view.
# file: views.py
class DisplayDataView(View):
data_load_service = None
def __init__(self, data_load_service):
self.data_load_service = data_load_service
def get(self, request):
return render(request, 'home/data_display.html', {
'data': this.data_load_service.data_display(),
}
You then build your url passing a DataLoad instance to your view
# file: urls.py
from core.dataload import DataLoad
data_load_service = DataLoad(...)
urls = [
path(
'data-load',
DisplayDataView.as_view(
data_load_service=data_load_service,
),
name='data-load',
),
]
This is the basic idea. I would personaly find another place for the DataLoad instanciation but it should do the trick for you until you find out a better way to organize all this.
Main point here is decoupling what your business logic is from the HTTP part (ie: request/response) of your application.
Related
I want to create a service using Django Rest API. I have a function. The result of this function should return 2 values and I should return these values in JSON API format.
The function will work like this. I will receive the features_list as a parameter and I will use it to create a result and display it as a service in json format in def prediction function.
I created a sample API (I guess) it is class PredictionSet in my views but I actually want to make service the def prediction function in my views.
I cannot understand how to apply it. I am so confused. Any help would be appreciated.
models.py
class Liquidity(models.Model):
pred_y = models.CharField(max_length=600)
score = models.FloatField()
views.py
class PredictionSet(viewsets.ModelViewSet):
queryset = Liquidity.objects.all()
serializer_class = LiquiditySerializer
def prediction(request, features_list):
filename = config.FINAL_MODEL_PATH
classifier = pickle.load(open(filename, 'rb'))
scale_file = config.SCALER_PATH
scaler = pickle.load(open(scale_file, 'rb'))
sample = np.array(features_list).reshape(1, -1)
sample_scaled = scaler.transform(sample)
pred_y = classifier.predict(sample_scaled)
prob_y = classifier.predict_proba(sample_scaled)
if prob_y[0][1] < 0.5:
score = 0
elif prob_y[0][1] <= 0.69:
score = 1
else:
score = 2
pred_y = pred_y[0]
prediction_obj = Liquidity.objects.get_or_create(pred_y=pred_y, score=score)
prediction_result = prediction_obj.pred_y
prediction_score = prediction_obj.score
context = {
'prediction_result ': prediction_result,
'prediction_score ': prediction_score,
}
return context
serializer.py
class LiquiditySerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Liquidity
fields = '__all__'
If you want to return custom JSON from a ModelViewset in DRF, you can override .list() and/or .retrieve() like this :
from rest_framework import status
from rest_framework.response import Response
class PredictionSet(viewsets.ModelViewSet):
queryset = Liquidity.objects.all()
serializer_class = LiquiditySerializer
# Your custom function definition
def prediction(self, request, features_list):
# The content
def retrieve(self, request, *args, **kwargs):
result = prediction(...) # Call your custom service and got result
# Return the result as JSON (url = /api/v1/predictions/1) an object
return Response({'data': result}, status=status.HTTP_200_OK)
def list(self, request, *args, **kwargs):
result = prediction(...) # Call your custom service and got result
# Return the result as JSON (url = /api/v1/predictions) a list of objects
return Response({'data': result}, status=status.HTTP_200_OK)
For more details follow this link
I have a ModelAdmin subclass for my gradeScalesSettings model:
#admin.register(gradeScalesSetting)
class gradeScalesSettingAdmin(admin.ModelAdmin):
list_display = ('configuration_select', 'NumberOfGrades', 'Rounding','Precision', 'Status',)
change_list_template = 'admin/Homepage/view.html'
Actual result
After I click Grade Scale Settings:
How to connect it to my views.py?
This is what I want to code in my views.py:
def gradescales(request):
gradeScalesSettings = gradeScalesSetting.objects.all()
configurations = configuration.objects.all()
rounding = gradeScalesSetting.objects.all().values_list('Rounding', flat=True).distinct()
print(rounding)
return render(request, 'Homepage/gradescale.html', {"rounding": rounding,"gradeScalesSetting":gradeScalesSettings,"configurations":configurations})
When I tried this:
#admin.register(gradeScalesSetting)
class gradeScalesSettingAdmin(admin.ModelAdmin):
def new_NumberOfGrades(self, obj):
if obj.NumberOfGrades == 'Grade Scale Settings':
return '<a href="view.html" </a>' # this url will redirect to your
In my ModelAdmin subclass:
list_display = ('configuration_select', 'new_NumberOfGrades', 'Rounding','Precision', 'Status',)
Is there any way to connect it to my views.py?
Expected result
This is what I want to show in my view.html:
That is why I want to connect it to my views.py.
Override ModelAdmin.changelist_view to set extra_context.
#admin.register(gradeScalesSetting)
class gradeScalesSettingAdmin(admin.ModelAdmin):
list_display = ('configuration_select', 'NumberOfGrades', 'Rounding','Precision', 'Status',)
change_list_template = 'admin/Homepage/view.html'
def changelist_view(self, request, extra_context=None):
extra_context = extra_context or {}
gradeScalesSettings = gradeScalesSetting.objects.all()
configurations = configuration.objects.all()
rounding = gradeScalesSetting.objects.all().values_list('Rounding', flat=True).distinct()
extra_context.update({
"rounding": rounding,
"gradeScalesSetting": gradeScalesSettings,
"configurations": configurations,
})
return super().changelist_view(request, extra_context=extra_context)
You can alter routing for ModelAdmin by overriding get_urls:
class gradeScalesSettingAdmin(admin.ModelAdmin):
def get_urls(self):
urls = super().get_urls()
pat = [i for i in urls if str(i.name).endswith('changelist')][0] # Find needed path object to replace
index = urls.index(pat)
urls[index] = path(pat.pattern._route, gradescales)
return urls
This way you can have full control of a view used for your admin page and even add additional pages (more than only changelist and edit views) as you want.
If you do not need additional default views like object edit page, you can simplify code above by replacing original urls instead of searching one needed and patching:
class gradeScalesSettingAdmin(admin.ModelAdmin):
def get_urls(self):
urls = [
path('', gradescales)
]
return urls
I defined a viewset using ModelViewSet as follow
I tried to redefine the GET method to do something like getting something from celery . but this part of code just won't work , it acts just like a standard API and didn't do what I wrote in the get_job_detail function.
How should I correctly define the "detail_route" function.
views.py
class JobViewSet(viewsets.ModelViewSet):
queryset = job.objects.all()
serializer_class = JobSerializer
#detail_route(methods=['get'])
def get_job_detail(self, request, pk=None):
# print('these part wont proceed')
job_item = self.get_object()
if job_item.isReady or job_item.isSuccessful:
return Response(self.serializer_class(job_item).data)
celeryjob = sometask.AsyncResult(pk)
celeryjob.get()
if celeryjob.state == 'SUCCESS':
job_item.state = celeryjob.state
job_item.result = celeryjob.result
job_item.isReady = True
job_item.isSuccessful = True
job_item.save()
if celeryjob.state == 'FAILURE':
job_item.state = celeryjob.state
job_item.result = celeryjob.result
job_item.isReady = True
job_item.isSuccessful = False
job_item.save()
return Response(self.serializer_class(job_item).data)
urls.py
from django.conf.urls import url, include
from apply_api import views
from rest_framework.routers import DefaultRouter
router = DefaultRouter()
router.register(r'job',views.JobViewSet)
urlpatterns = [
url(r'^', include(router.urls)),
]
now your correct url is: /job/<pk>/get_job_detail if you want just: /job/<pk> you don need to use #detail_route just rename your method to the def retrieve(self, request, *args, **kwargs): more details retrievemodelmixin one of the part class of the modelviewset
A project using Django, DRF, swagger.
url config below:
schema_view = get_swagger_view(title='Pastebin API')
urlpatterns = [
url(r'^$', schema_view),
url(r'store', include('store.urls')),
... # other urls using routers.SimplerRouter
]
and store/urls.py:
router = routers.SimpleRouter()
router.register(r'', views.StoreViewSet)
urlpatterns = router.urls
and views.StoreViewSet:
class StoreViewSet(BaseObject, GenericViewSet):
permition_class = ()
#list_route()
def detail(self, request):
return {}
#list_route(url_path='detail/export')
def detail_export(self, request):
return {}
after python manage.py runserver, visit http://127.0.0.1:8000/# and a TypeError occured:
File "/usr/local/share/.virtualenvs/dev-finance/lib/python2.7/site-packages/rest_framework_swagger/views.py", line 32, in get
schema = generator.get_schema(request=request)
File "/usr/local/share/.virtualenvs/dev-finance/lib/python2.7/site-packages/rest_framework/schemas.py", line 242, in get_schema
links = self.get_links(request)
File "/usr/local/share/.virtualenvs/dev-finance/lib/python2.7/site-packages/rest_framework/schemas.py", line 276, in get_links
insert_into(links, keys, link)
File "/usr/local/share/.virtualenvs/dev-finance/lib/python2.7/site-packages/rest_framework/schemas.py", line 79, in insert_into
target[keys[-1]] = value
TypeError: 'Link' object does not support item assignment
[ERROR] 2017-05-04 15:25:06,936 log_message(basehttp.py:131) "GET / HTTP/1.1" 500 20384
The error message shows, the Link object can't assign value, like dict does.
If I rename the method name from detail_export to details_export, everything goes fine again.
Guessing error occured when rest_framework's #list_route decorater
transfer the method's url to Link object.
why other methods just goes fine?
how can I solve this?
This is the bug in DRF. Probably, it will be fixed in 3.6.4. Now you can:
Delete url_path from list_route:
class StoreViewSet(BaseObject, GenericViewSet):
permition_class = ()
#list_route()
def detail(self, request):
return {}
#list_route()
def detail_export(self, request):
return {}
Or use a custom SchemaView with a custom SchemaGenerator:
# api/schema.py for example
from rest_framework.permissions import AllowAny
from rest_framework.renderers import CoreJSONRenderer
from rest_framework.response import Response
from rest_framework.schemas import SchemaGenerator
from rest_framework.views import APIView
from rest_framework_swagger import renderers
class CustomSchemaGenerator(SchemaGenerator):
def get_keys(self, subpath, method, view):
result = super().get_keys(subpath, method, view)
# here you can fix your path
if result == ['store', 'detail', 'detail_export']:
return ['store', 'detail_export']
return result
class SwaggerSchemaView(APIView):
_ignore_model_permissions = True
exclude_from_schema = True
permission_classes = [AllowAny]
renderer_classes = [
CoreJSONRenderer,
renderers.OpenAPIRenderer,
renderers.SwaggerUIRenderer
]
def get(self, request):
generator = CustomSchemaGenerator(title='API')
return Response(generator.get_schema(request=request))
# urls.py
from django.contrib.auth.decorators import login_required
from api.schema import SwaggerSchemaView
urlpatterns = [
# ...
url(r'^swagger-url/', login_required(SwaggerSchemaView.as_view()))
# ...
]
Here's a hack:
Open
your-environment-folder/lib/python3.5/site-packages/rest_framework/schemas.py
Find "insert_into" function
Replace this:
target[keys[-1]] = value
with this:
if type(target) != coreapi.Link:
target[keys[-1]] = value
I am using Django-PuSH (PubSubHubPub) for my Blogger's Blog.
Now, I read the documentation and could make simple code. However, the document never mentioned anywhere to enter a callback URL!
Here is the simple code.
# hubserver.py
#receiver(updated)
def listener(notification, **kwargs):
e = []
for entry in notification.entries:
e.append(entry.title)
for link in e:
db = entries( url = link )
db.save(force_insert=True)
from django.shortcuts import render_to_response
from django_push.subscriber.models import Subscription
from models import mydata
# views
def bloghub (request):
subscription = Subscription.objects.subscribe("http://myblog.blogspot.in//feeds/posts/default",
"http://pubsubhubbub.appspot.com")
db = mydata.objects.all()
lis = [i.url for i in db]
context = {"lis" : lis}
return render_to_response("home.html", context)
[1]: http://django-push.readthedocs.org
# urls.py
urlpatterns = patterns('',
url(r'^$', views.bloghub ),
url(r'^subscriber/', include('django_push.subscriber.urls')),
)