Call a function using value from variable - python

I want to get a model in django using the current url
for example
a = resolve(request.path_info).url_name
context = { 'example' : a.objects.all }
here the retrieved url name is available in models
This obivously won't work but this is what I want to accomplish

You can access the request's resolver via request.resolver_match. See the docs here for more info. That can give you better access to the resolver than looking it up again.
Once you have that you can look up the model using the AppConfig.get_model method.
from django.apps import apps
ModelClass = apps.get_model(app_name, model_name)
Or
ModelClass = apps.get_model(f'{app_name}:{model_name}')

Related

Django REST can't write custom Throttle and getting ImportError:

folder structure api/api_views.py
api_views.py
class BurstRateThrottle(UserRateThrottle):
scope = 'burst'
class SustainedRateThrottle(UserRateThrottle):
scope = 'sustained'
settings.py
REST_FRAMEWORK = {
'DEFAULT_THROTTLE_CLASSES': [
'api.api_views.BurstRateThrottle',
'api.api_views.SustainedRateThrottle'
],
'DEFAULT_THROTTLE_RATES': {
'burst': '60/min',
'sustained': '1000/day'
}
}
Getting this error : Could not import 'api.api_views.BurstRateThrottle' for API setting 'DEFAULT_THROTTLE_CLASSES'. ImportError: Module "api.api_views" does not define a "BurstRateThrottle" attribute/class.
I would suggest checking the official documentation which is very straightforward.
https://www.django-rest-framework.org/api-guide/throttling/#custom-throttles
Moreover, There are certain models that need to be overridden in order to be able to implement a fully working custom throttling solution. However, You can use the built-in functionality that throttling modules offer and customize it based on your needs.
Simple Example:
from rest_framework.throttling import UserRateThrottle
class ThrottleCheck(APIView):
throttle_class = UserRateThrottle
throttle_scope = "THIS SHOULD BE THE NAME YOU DEFINE IN YOUR SETTING FILE"
def get(self,request:Request)-> Response:
"""
SOME CODE GOES HERE.
The throttle would be triggered here
"""
The last words, Ideally if you can handle throttling from your Nginx side would make more sense Since on production this might be challenging using the Throttle class of Django-rest-framework.
Hope this helps. Happy Hacking

Django - how can i call a model as a string?

I'm building a DRF api endpoint that should render some json data from my database. In my view, the name of the model to query to render the data is not fixed, it depends on what query does the user perform.
So i have this:
collection = 'MY_MODEL'
queryset = collection.objects.filter(**filters)
But this will give the following error:
'str' object has no attribute 'objects'
This will work instead: queryset = MY_MODEL.objects.filter(**filters)
Is there any way to get around this?
Django actually has a method to do this:
from django.utils.module_loading import import_string
ModelClass = import_string('yourapp.models.YOUR_MODEL')
queryset = ModelClass.objects.filter(**filters)
Note that you have to provide the full path of your model.
The docs: https://docs.djangoproject.com/en/3.1/ref/utils/#django.utils.module_loading.import_string
If you only have a few models you need to support however I would actually advise to just use a dictionary that you maintain yourself:
MODEL_MAPPING = {
"model_a": MyModelAClass,
"model_b": MyModelBClass,
}
user_input = "model_b"
ModelClass = MODEL_MAPPING.get(user_input)
if ModelClass is not None:
queryset = ModelClass.objects.filter(**filters)

Wagtail: Creating a custom API endpoint

I've created a Snippet called "Spotlights," and I'm wondering how I can create a custom endpoint for Snippet data with the Wagtail API. My best guess would be:
api_router.register_endpoint('Spotlights', BaseAPIEndpoint)
Is the first arg there establishing the name of the endpoint, or referencing something?
I've figured it out: just subclass Wagtail's BaseAPIEndpoint. For example:
endpoints.py
from wagtail.api.v2.endpoints import BaseAPIEndpoint
class SpotlightsAPIEndpoint(BaseAPIEndpoint):
...
model = Spotlight
api.py
from .endpoints import SpotlightsAPIEndpoint
api_router.register_endpoint('spotlights', SpotlightsAPIEndpoint)
Plus there are tons of ways to customize it. Just take a look at the endpoints.py file in the Wagtail repo: https://github.com/wagtail/wagtail/blob/master/wagtail/api/v2/endpoints.py
According to Wagtail documentation, the first parameter is the name of the endpoint (eg. pages, images) and this is used in the URL of the endpoint.
The second parameter is the endpoint class that handles the requests.
For example:
api_router.register_endpoint('pages', PagesAPIEndpoint)
api_router.register_endpoint('images', ImagesAPIEndpoint)
api_router.register_endpoint('documents', DocumentsAPIEndpoint)
So, I advise you to make like:
api_router.register_endpoint('spotlights', BaseAPIEndpoint)
Latest Wagtail version - 2.15 +
in your views file, import your model and BaseApiViewSet
from .models import CustomModel
from wagtail.api.v2.views import BaseAPIViewSet
class BusinessLocationViewSet(BaseAPIViewSet):
model = BusinessLocation
body_fields = BaseAPIViewSet.body_fields + ['id', 'field1', 'field2', 'field3', 'field4', etc, etc...]
and in the api.py file in the project folder, import your model and expose it to the api as follows:
from custommodel.views import MyModel
api_router.register_endpoint('custom', MyModel)
and now your model will be accessible from the api/v2/custom endpoint
I needed to modify glls's solution a bit to make it work. To expose the endpoint in api.py:
from .views import CustomViewSet
api_router.register_endpoint("custom", CustomViewSet)
It's also common to add filtering functionality. In views.py:
from wagtail.images.api.v2.views import BaseAPIViewSet, FieldsFilter
from .models import Custom
class CustomViewSet(BaseAPIViewSet):
model = Custom
body_fields = BaseAPIViewSet.body_fields + [
"name"
]
filter_backends = [
FieldsFilter
]
You can now filter the Custom snippet like normal: /api/v2/custom/?name=something

Mocking models used in a Django view with arguments

For the life of me I cannot figure this out and I'm having trouble finding information on it.
I have a Django view which accepts an argument which is a primary key (e.g: URL/problem/12) and loads a page with information from the argument's model.
I want to mock models used by my view for testing but I cannot figure it out, this is what I've tried:
#patch('apps.problem.models.Problem',)
def test_search_response(self, problem, chgbk, dispute):
problem(problem_id=854, vendor_num=100, chgbk=122)
request = self.factory.get(reverse('dispute_landing:search'))
request.user = self.user
request.usertype = self.usertype
response = search(request, problem_num=12)
self.assertTemplateUsed('individual_chargeback_view.html')
However - I can never get the test to actually find the problem number, it's as if the model does not exist.
I think that's because if you mock the entire model itself, the model won't exist because any of the functions to create/save it will have been mocked. If Problem is just a mock model class that hasn't been modified in any way, it knows nothing about interacting with the database, the ORM, or anything that could be discoverable from within your search() method.
One approach you could take rather than mocking models themselves would be to create FactoryBoy model factories. Since the test database is destroyed with each test run, these factories are a great way to create test data:
http://factoryboy.readthedocs.io/en/latest/
You could spin up a ProblemFactory like so:
class ProblemFactory(factory.Factory):
class Meta:
model = Problem
problem_id = factory.Faker("pyint")
vendor_num = factory.Faker("pyint")
chgbk = factory.Faker("pyint")
Then use it to create a model that actually exists in your database:
def test_search_response(self, problem, chgbk, dispute):
problem = ProblemFactory(problem_id=854, vendor_num=100, chgbk=122)
request = self.factory.get(reverse('dispute_landing:search', kwargs={'problem_id':problem.id}))
request.user = self.user
request.usertype = self.usertype
response = search(request, problem_num=854)
self.assertTemplateUsed('individual_chargeback_view.html')

django-tastypie usage without model

I need to POST data via AJAX-request to backend python function (that data will be processed with third-party python script) and use the result in the frontend. Currently I am using django-tastypie for API (I am using only ModelResource for my Models). As I understand, I can use Resource to implement this behaviour, but I am a little confused because I don't want to save or store any data, I just want to procced it in the backend. Should I use django-tastypie or maybe it is better to choose another method?
You can use prepend_urls for this
prepend_urls -> A hook for adding your own URLs or matching before the default URLs. Useful for adding custom endpoints or overriding the built-in ones. Tastypie docs link
See below code
class YourModelResource(ModelResource):
class Meta:
queryset = YourModel.objects.all()
resource_name = 'your_model'
def prepend_urls(self):
return [
url(r"^(?P<resource_name>%s)/do_some_work%s$" % (self._meta.resource_name, trailing_slash()), self.wrap_view('do_some_work'), name="api_do_some_work"),
]
def do_some_work(self, request, **kwargs):
self.method_check(request, allowed=['post'])
self.is_authenticated(request)
#Call the script and get the results
results = []
result_list = {
'results': results,
}
return self.create_response(request, result_list)
Here prepend_urls method is overridden to call a nested resource do_some_work. The URI for this call will be like this
/api/v1/your_model/do_some_work/
Above method is suggested if you have to use Tastypie other wise Django views will be best option for this scenario.

Categories

Resources