How to resend the same request again in Django api - django

I have a BuyproducTviewset using createModelMixin that create an instance when post request is made however I want to repeat the same create request again after 5 seconds from the api depending on a condition if the price is greater than a specific range.
class BuyProductViewset(viewsets.GenericViewSet, mixins.CreateModelMixin):
serializer_class = UserproductSerializer
queryset = Userproducts.objects.all()
def data_serialize(self, user_id, product_ID):
data = {"user": user_id, "product": product_ID}
serializer = self.get_serializer(data=data)
serializer.is_valid(raise_exception=True)
self.perform_create(serializer)
headers = self.get_success_headers(serializer.data)
return serializer, headers
def create(self, request, *args, **kwargs):
user_id = request.data["user_id"]
product_id = request.data["product_id"]
total = request.data["total"]
upper_bound = request.data["upper_bound"]
lower_bound = request.data["lower_bound"]
product = (
product.objects.filter(product_id=product_id)
.order_by("timestamp")
.reverse()[:1]
.values("id", "name", "price", "availability")
.get()
)
product_ID = product["id"]
product_price = product["price"]
product_availability = product["availability"]
product_name = product["name"]
if product_price >= int(lower_bound) and product_price <= int(upper_bound):
serializer, headers = self.data_serialize(user_id,
product_ID)
product.objects.create(
product_id=product_ID,
name=product_name,
price=product_price,
availability=product_availability - int(total),
)
return Response(
serializer.data, status=status.HTTP_201_CREATED,
headers=headers
)
else:
time.sleep(5)
# RESEND THE SAME REQUEST WITH THE SAME DATA
return a response

if you resend the same request, it can be a cause for infinity loop.
But you can do it:
def create(self, request, *args, **kwargs):
...
else:
time.sleep(5)
# RESEND THE SAME REQUEST WITH THE SAME DATA
# here should be a reason to stop the resend process
return self.create(request, *args, **kwargs)
Don't forget. This code is synchronous. time.sleep(5) blocks other processes for 5sec.

Related

What is the most effective validation method on Flask?

First of all, I try to make an Rest-API with Flask MongoEngine. I create a model which is;
class Project(db.Document):
name = db.StringField(validation=validate_project_name)
created_at = db.DateTimeField()
updated_at = db.DateTimeField()
def save(self, *args, **kwargs):
if not self.created_at:
self.created_at = datetime.datetime.now()
self.updated_at = datetime.datetime.now()
return super(Project, self).save(*args, **kwargs)
def __str__(self):
return self.name
and I also created a Form ;
ProjectForm = model_form(Project, exclude=["created_at", "updated_at"])
and also the custom validation is ;
def validate_project_name(name, min=5, max=25):
if len(name) <= min or len(name) >= max:
raise ValidationError(
f"Proje adı en az {min} en fazla {max} karakter olabilir!"
)
return True
I want to validate with ProjectFrom when i send the data which I get from request body that's why i prepare that code ;
#project.route("/create", methods=["POST"])
def create_project():
data = MultiDict(request.json)
form = ProjectForm(data, meta={"csrf": False})
if form.validate():
if Project(name=form.data["name"]).save():
## will take create action
return True
else:
return Response({form.errors}, status=400)
It returns true when i request but when i change de data on request.body it still returns true where do i mistake can someone help me and explain the logic. Or Should i create a middleware for the validation also i tried that. and i created something. But i don't know is it the best way.
Thanks in advance...
Middleware that i tried to validate data;
def creation_project_validation(func):
#wraps(func)
def decorated_function(*args, **kwargs):
if request.method == "POST":
data = request.json
if data.get("name") is None:
return Response({"Name is required field!"}, status=400)
if validate_project_name(data.get("name")):
return Response(
{"Karakter uzunluğu istenilen şekilde değil!"}, status=400
)
return func(*args, **kwargs)
else:
Response("Method not allowed", status=405)
return decorated_function

mail chimp subscription not working with django

Hi everyone I integrate my Django Web app with mail chimp . in my admin panel when I open marketing preference it give me error . and it do not subscribe my users when I click on subscribe my user remain unsubscribe when i hit save .the error I got is
{'type': 'https://mailchimp.com/developer/marketing/docs/errors/', 'title': 'Invalid Resource', 'status': 400, 'detail': "The resource submitted could not be validated. For field-specific details, see the 'errors' array.", 'instance': '2b647b4f-6e58-439f-8c91-31a3223600a9', 'errors': [{'field': 'email_address', 'message': 'This value should not be blank.'}]}
my models.py file is:
class MarketingPreference(models.Model):
user =models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
subscribed =models.BooleanField(default=True)
mailchimp_subscribed = models.NullBooleanField(blank=True)
mailchimp_msg =models.TextField(null=True , blank=True)
timestamp =models.DateTimeField(auto_now_add=True)
updated =models.DateTimeField(auto_now=True)
def __str__(self):
return self.user.email
def marketing_pref_create_reciever(sender, instance, created, *args, **kwargs):
if created:
status_code, response_data = Mailchimp().subscribe(instance.user.email)
print(status_code, response_data)
post_save.connect(marketing_pref_create_reciever, sender=MarketingPreference)
def marketing_pref_update_reciever(sender, instance, *args, **kwargs):
if instance.subscribed != instance.mailchimp_subscribed:
if instance.subscribed:
#subscribing user
status_code, response_data = Mailchimp().subscribe(instance.user.email)
else:
#unsubscribing user
status_code, response_data = Mailchimp().unsubscribe(instance.user.email)
if response_data['status'] =='subscribed':
instance.subscribed = True
instance.mailchimp_subscribed = True
instance.mailchimp_msg = response_data
else:
instance.subscribed = False
instance.mailchimp_subscribed = False
instance.mailchimp_msg = response_data
pre_save.connect(marketing_pref_update_reciever, sender=MarketingPreference)
def make_marketing_pref_reciever(sender, instance, created, *args, **kwargs):
if created:
MarketingPreference.objects.get_or_create(user=instance)
post_save.connect(make_marketing_pref_reciever , sender=settings.AUTH_USER_MODEL)
my utils.py is:
MAILCHIMP_API_KEY = getattr(settings, "MAILCHIMP_API_KEY" , None)
MAILCHIMP_DATA_CENTER = getattr(settings, "MAILCHIMP_DATA_CENTER" , None)
MAILCHIMP_EMAIL_LIST_ID = getattr(settings, "MAILCHIMP_EMAIL_LIST_ID" , None)
def check_email(email):
if not re.match(r".+#.+\..+",email):
raise ValueError("String passed is not a valid email address")
return email
def get_subscriber_hash(member_email):
#check email
check_email(member_email)
member_email = member_email.lower().encode()
m = hashlib.md5(member_email)
return m.hexdigest()
class Mailchimp(object):
def __init__(self):
super(Mailchimp, self).__init__()
self.key = MAILCHIMP_API_KEY
self.api_url = "https://{dc}.api.mailchimp.com/3.0/".format(dc=MAILCHIMP_DATA_CENTER)
self.list_id = MAILCHIMP_EMAIL_LIST_ID
self.list_endpoint = '{api_url}/lists/{list_id}'.format(api_url=self.api_url, list_id=self.list_id)
def get_members_endpoint(self):
return self.list_endpoint + "/members"
def change_subscription_status(self, email, status='unsubscribed'):
hashed_email = get_subscriber_hash(email)
endpoint = self.get_members_endpoint() +"/" + hashed_email
data = {
"status":self.check_valid_status(status)
}
r = requests.put(endpoint, auth=("",self.key), data=json.dumps(data))
return r.status_code, r.json()
def check_subscription_status(self,email):
hashed_email = get_subscriber_hash(email)
endpoint = self.get_members_endpoint() +"/" + hashed_email
r = requests.get(endpoint, auth=("", self.key))
return r.status_code, r.json()
def check_valid_status(self, status):
choices = ['subscribed' , 'unsubscribed', 'cleaned' , 'pending']
if status not in choices:
raise ValueError("not a valid choice for email status")
return status
def add_email(self,email):
status = "subscribed"
self.check_valid_status(status)
data = {
"email_address":email,
"status": status
}
endpoint = self.get_members_endpoint()
r = requests.post(endpoint, auth=("",self.key), data=json.dumps(data))
return self.change_subscription_status(email, status='subscribed')
def unsubscribe(self, email):
return self.change_subscription_status(email, status='unsubscribed')
def subscribe(self, email):
return self.change_subscription_status(email, status='subscribed')
def pending(self, email):
return self.change_subscription_status(email, status='pending')
mixins.py is:
from django.utils.decorators import method_decorator
from django.views.decorators.csrf import csrf_exempt
class CsrfExemptMixin(object):
#method_decorator(csrf_exempt)
def dispatch(self, request,*args, **kwargs):
return super(CsrfExemptMixin, self).dispatch(request,*args, **kwargs)
and my views.py is:
from .mixins import CsrfExemptMixin
from .models import MarketingPreference
from . utils import Mailchimp
MAILCHIMP_EMAIL_LIST_ID = getattr(settings, "MAILCHIMP_EMAIL_LIST_ID" , None)
# Create your views here.
class MarketingPreferenceUpdateView(SuccessMessageMixin, UpdateView):
form_class = MarketingPreferenceForm
template_name = 'base/forms.html'
success_url = '/settings/email/' #this is a builtin method and by default it will go to marketig preference
success_message = 'Your email preferences have been updated. Thank you'
def dispatch(self, *args, **kwargs): #when user came with incognito email will not show to him it will redirect him back to login page
user = self.request.user
if not user.is_authenticated:
return redirect("/login/?next=/settings/email/") #HttpResponse("not allowed", status=400)
return super(MarketingPreferenceUpdateView, self).dispatch(*args,**kwargs)#(request, *args...)
def get_context_data(self, *args, **kwargs):
context = super(MarketingPreferenceUpdateView, self).get_context_data(*args,**kwargs)
context['title'] = 'Update Email Preference'
return context
def get_object(self):
user = self.request.user
obj , created = MarketingPreference.objects.get_or_create(user=user)
return obj
class MailchimpWebhookView(CsrfExemptMixin,View): #it will not work because our web application is not deployes yet and webhook mailchimp do not work with local host
#def get(self, *args, **kwargs):
# return HttpResponse('thank you', status=200)
def post(self, request, *args, **kwargs):
data = request.POST
list_id = data.get('data[list_id]')
if str(list_id) == str(MAILCHIMP_EMAIL_LIST_ID): # I CHECK THAT DATA DATA IS THE RIGHT LIST
hook_type = data.get("type")
email = data.get('data[email]')
response_status, response = Mailchimp().check_subscription_status(email)
sub_status = response['status']
is_subbed = None
mailchimp_subbed = None
if sub_status == "subscribed":
is_subbed, mailchimp_subbed = (True, True)
elif sub_status == "unsubscribed":
is_subbed, mailchimp_subbed = (False, False)
if is_subbed is not None and mailchimp_subbed is not None:
qs = MarketingPreference.objects.filter(user__email__iexact=email)
if qs.exists():
qs.update(subscribed=is_subbed, mailchimp_subscribed=mailchimp_subbed, mailchimp_msg=str(data))
return HttpResponse('thank you', status=200)
Here you have already assigned request.POST to data which is now a dictonary, so to get a value from dictonary you should use the field name of the form widget, as data == request.POST now.
Problem is you are getting wrong key.So your email will always be empty
list_id = data.get('data[list_id]')
email = data.get('data[email]')#You are getting wrong key
It should be like this
list_id = data.get('list_id')
email = data.get('email')

DRF, remove primary key from routed extra action

I have a view with some extra actions:
class MonthsViewSet(ModelViewSet):
authentication_classes = (TokenAuthentication,)
def get_queryset(self):
query_set = Month.objects.filter(user=self.request.user)
return query_set
serializer_class = MonthSerializer
#swagger_auto_schema(
manual_parameters=[AUTH_HEADER_PARAM, MonthParameters.DATE, MonthParameters.DAYS, MonthParameters.FARM])
def create(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
self.perform_create(serializer)
headers = self.get_success_headers(serializer.data)
return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
#action(detail=True)
def get_next_year(self, *args, **kwargs):
"""
Return the next 12 months.
"""
first_month, last_month = get_12_months(last=False)
query_set = self.get_queryset().filter(date__range=(first_month, last_month))
serializer = MonthSerializer(query_set, many=True)
return Response(serializer.data, status.HTTP_200_OK)
#action(detail=True)
def get_last_year(self, *args, **kwargs):
"""
Return the last 12 months available.
"""
first_month, last_month = get_12_months(last=True)
print(first_month, last_month)
query_set = self.get_queryset().filter(date__range=(first_month, last_month))
serializer = MonthSerializer(query_set, many=True)
return Response(serializer.data, status.HTTP_200_OK)
And I'm using the default router in my url:
months_router = DefaultRouter()
months_router.register('months', MonthsViewSet, 'months')
urlpatterns = [
path('', include(months_router.urls)),
]
So currently this is my URL:
/months/{date}/get_last_year/
the date is the primary key in my model.
Is there any way to change the action decorator settings to NOT use the primary key?
so the URL would become:
/months/get_last_year/
From the DRF doc,
Like regular actions, extra actions may be intended for either a single object, or an entire collection. To indicate this, set the detail argument to True or False. The router will configure its URL patterns accordingly.
set detail=False in your decorator.
#action(detail=False)
def get_last_year(self, *args, **kwargs):
# res of your code

Django rest-framework with GenericViewSet: Filter results base on query parameters from the url

My application is using GenericViewSet with ListModelMixin. I have used filter_backends and filter_class to filter out results. (see 'list': serializers.BookingListSerializer from screenshot below)
I am working on the following brief:
Let's say I have a list of animals which are pre-filtered (using filter_backends) and then shown on UI to the user.
Users can further filter results based on some search criteria from UI (let's say name, type, color). These filterations are handled by filter_class.
In a separate Tab on UI which only shows animals of type Dogs rather than the entire collection of animals. And which can again be filtered further based on the name & color.
I must create 2 separate end-points to show both kinds of results to the user (to have more control over results...ya screw DRY!). But I can't figure out how to create them in Django as both animals and dogs use the same django modal and the filter backends and filter class are applied only to the actual modal ie. on the list of animals.
I need simple def list1(request) and def list2(request) where I can filter the query_set based on request params and my filter backends and filter classes.
api.py
class BookingViewSet(
MultipleSerializerMixin,
mixins.CreateModelMixin,
mixins.RetrieveModelMixin,
mixins.UpdateModelMixin,
mixins.ListModelMixin,
viewsets.GenericViewSet
):
lookup_field = 'uuid'
queryset = models.Booking.objects.all()
permission_classes = [DRYPermissions, ]
filter_backends = [filters.BookingFilterBackend, DjangoFilterBackend, ]
filter_class = filters.BookingFilter
pagination_class = BookingViewSetPagination
serializer_class = serializers.BookingDetailSerializer
serializer_classes = {
'create': serializers.BookingCreateUpdateSerializer,
'update': serializers.BookingCreateUpdateSerializer,
'duplicate': serializers.BookingCreateUpdateSerializer,
'list': serializers.BookingListSerializer,
'list_drafts': serializers.BookingListSerializer,
'create_draft': serializers.BookingCreateUpdateSerializer,
'submit_draft': serializers.BookingCreateUpdateSerializer,
}
def create(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
booking = services.create_booking(serializer.validated_data)
data = serializers.BookingDetailSerializer(booking, context={'request': request}).data
return response.Created(data)
def update(self, request, *args, **kwargs):
booking = self.get_object()
partial = kwargs.pop('partial', False)
serializer = self.get_serializer(booking, data=request.data, partial=partial)
serializer.is_valid(raise_exception=True)
booking = services.update_booking(booking, serializer.validated_data)
async('shootsta.bookings.tasks.booking_update_google_calendar_event', booking.pk)
data = serializers.BookingDetailSerializer(booking, context={'request': request}).data
return response.Ok(data)
#detail_route(methods=['POST'], url_path='duplicate')
def duplicate(self, request, *args, **kwargs):
booking = self.get_object()
new_booking = services.duplicate_booking(booking)
data = serializers.BookingDetailSerializer(new_booking, context={'request': request}).data
return response.Created(data)
#list_route(methods=['GET'], url_path='list-drafts')
def list_drafts(self, request, *args, **kwargs):
# Code goes here! Here i'll get some params from url like state and title and then return filtered the results.
pass
#list_route(methods=['POST'], url_path='create-draft')
def create_draft(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
booking = services.create_booking(serializer.validated_data, constants.BookingMode.draft)
data = serializers.BookingDetailSerializer(booking, context={'request': request}).data
return response.Created(data)
#detail_route(methods=['POST'], url_path='submit-draft')
def submit_draft(self, request, *args, **kwargs):
booking = self.get_object()
booking.submit_draft(by=request.user)
booking.save()
data = serializers.BookingDetailSerializer(booking, context={'request': request}).data
return response.Ok(data)
#detail_route(methods=['POST'], url_path='approve')
def approve(self, request, *args, **kwargs):
booking = self.get_object()
booking.approve(by=request.user)
booking.save()
data = serializers.BookingDetailSerializer(booking, context={'request': request}).data
return response.Ok(data)
filters.py
# Standard Library
import operator
from functools import reduce
# Third Party
from django.db.models import Q
from django_filters import rest_framework as filters
from dry_rest_permissions.generics import DRYPermissionFiltersBase
# Project Local
from . import models
class BookingFilterBackend(DRYPermissionFiltersBase):
def filter_list_queryset(self, request, queryset, view):
if request.user.is_role_admin:
return queryset
if request.user.is_role_client:
return queryset.filter(Q(client=request.user.client))
if request.user.is_role_camop:
return queryset.filter(Q(camera_operator=request.user))
return queryset.filter(Q(created_by=request.user))
def filter_booking_title(queryset, name, value):
"""
Split the filter value into separate search terms and construct a set of queries from this. The set of queries
includes an icontains lookup for the lookup fields for each of the search terms. The set of queries is then joined
with the OR operator.
"""
lookups = ['title__icontains', ]
or_queries = []
search_terms = value.split()
for search_term in search_terms:
or_queries += [Q(**{lookup: search_term}) for lookup in lookups]
return queryset.filter(reduce(operator.or_, or_queries))
class BookingFilter(filters.FilterSet):
title = filters.CharFilter(method=filter_booking_title)
class Meta:
model = models.Booking
fields = [
'title',
'state',
'client',
]
class SampleViewset(.....):
#list_route(methods=['GET'])
def list_2(self, request, *args, **kwargs):
myqueryset = MyModel.objects.all() # or whatever queryset you need to serialize
queryset = self.filter_queryset(myqueryset)
page = self.paginate_queryset(queryset)
if page is not None:
serializer = self.get_serializer(page, many=True)
return self.get_paginated_response(serializer.data)
serializer = self.get_serializer(queryset, many=True)
return Response(serializer.data)
The key points you should notice here are,
1. The filtering process are being excecuted inside the self.filter_queryset() method, which return a QuerySet after filter applied.
2. You could use self.get_queryset() method in place of myqueryset = MyModel.objects.all() staement, which is the DRF Way of doing such things
UPDATE-1
If you want to use the default queryset , you could use the get_queryset() method as,
class SampleViewset(.....):
#list_route(methods=['GET'])
def list_2(self, request, *args, **kwargs):
queryset = self.filter_queryset(self.get_queryset())
page = self.paginate_queryset(queryset)
if page is not None:
serializer = self.get_serializer(page, many=True)
return self.get_paginated_response(serializer.data)
serializer = self.get_serializer(queryset, many=True)
return Response(serializer.data)
or simply,
class SampleViewset(.....):
#list_route(methods=['GET'])
def list_2(self, request, *args, **kwargs):
return self.list(self, request, *args, **kwargs)
I didn't quite get the question but I think you should do the same thing on your custom action that DRF does on its generic list. just call filter_queryset on your initial query for example:
class your_view(....):
...
...
def get_queryset2(self):
return YourotherModel.objects.all() ### or any thing your i.e. specific fiter on your general model
#action(methods=['GET'], detail=False)
def list2(self, request, *args, **kwargs):
queryset = self.filter_queryset(self.get_queryset2()) ### call filter_queryset on your custom query
page = self.paginate_queryset(queryset)
if page is not None:
serializer = self.get_serializer(page, many=True)
return self.get_paginated_response(serializer.data)
serializer = self.get_serializer(queryset, many=True)
return Response(serializer.data)

Tastypie to respond for every requests in Json format

I am working on Api for my project, i'm using Tastypie 9.9.0. I want the response in Json format for PUT, POST and DELETE operations.
The existing responses like STATUS 201 CREATED, STATUS 204 NO CONTENT, STATUS 410 GONE is fine.
It must respond in a custom format.
for example
1. {
"resource_name": "user",
"action":"password_reset",
"status": "success"
}
2. {
"resource_name": "todo",
"action":"insert",
"status":"sucess",
}
3. {
"resource_name": "todo",
"action":"delete",
"status":"sucess",
}
this is the code i was working on. I dont know how to add custom response messages
class ToDoResource(ModelResource):
user = fields.ToOneField(UserResource, 'user')
class Meta:
queryset = ToDo.objects.all()
fields=['alert_time','description','status','user']
resource_name = 'todo'
filtering = {
'user': ALL_WITH_RELATIONS,
'alert_time': ['exact', 'range', 'gt', 'gte', 'lt', 'lte'],
}
serializer = Serializer()
authentication= MyApiKeyAuthentication()
authorization=Authorization()
always_return_data = True
allowed_methods = ['post','get','put','delete']
def obj_create(self, bundle,request=None, **kwargs):
if not request.user.is_superuser:
try:
bundle.data.pop('user')
except:
pass
return super(ToDoResource, self).obj_create(bundle, request, user=request.user)
def create_response(self, request, data):
"""
Extracts the common "which-format/serialize/return-response" cycle.
Mostly a useful shortcut/hook.
"""
desired_format = self.determine_format(request)
serialized = self.serialize(request, data, desired_format)
return HttpResponse(content=serialized, content_type=build_content_type(desired_format))
def apply_authorization_limits(self, request, object_list):
return object_list.filter(user=request.user)
You can add/modify custom data in get_list(request, **kwargs) and/or get_object(request, **kwargs)
For example,
import json
from django.http import HttpResponse
class ToDoResource(ModelResource):
# ... the rest of code
def get_list(self, request, **kwargs):
resp = super(ToDoResource, self).get_list(request, **kwargs)
data = json.loads(resp.content)
# ... the rest of code
data['meta']['resource_name'] = self._meta.resource_name
data['meta']['action'] = request.method
data['meta']['status'] = ANY_STATUS
# ... the rest of code
data = json.dumps(data)
return HttpResponse(data, mimetype='application/json', status=ANY_STATUS)