Showing posts with label django-rest-framework. Show all posts
Showing posts with label django-rest-framework. Show all posts

Tuesday, August 7, 2018

Django Rest Framework - passing Model data through a function, then posting output in a separate field in the same model

Leave a Comment

(Django 2.0, Python 3.6, Django Rest Framework 3.8)

I'm trying to fill the calendarydays field in the model below:

Model

class Bookings(models.Model):     booked_trainer = models.ForeignKey(TrainerProfile, on_delete=models.CASCADE)     booked_client = models.ForeignKey(ClientProfile, on_delete=models.CASCADE)     trainer_availability_only = models.ForeignKey(Availability, on_delete=models.CASCADE)     calendarydays = models.CharField(max_length=300, blank=True, null=True)      PENDING = 'PENDING'     CONFIRMED = 'CONFIRMED'     CANCELED = 'CANCELED'      STATUS_CHOICES = (         (PENDING, 'Pending'),         (CONFIRMED, 'Confirmed'),         (CANCELED, 'Canceled')     )       booked_status = models.CharField(         max_length = 9,         choices = STATUS_CHOICES,         default = 'Pending'     )      def __str__(self):         return str(self.trainer_availability_only) 

Now, I have a function that takes values from trainer_availability_only and converts those values to a list of datetime strings, the returned output would look like this:

{'calendarydays': ['2018-07-23 01:00:00', '2018-07-23 02:00:00', '2018-07-23 03:00:00', '2018-07-30 01:00:00', '2018-07-30 02:00:00', '2018-07-30 03:00:00', '2018-08-06 01:00:00', '2018-08-06 02:00:00', '2018-08-06 03:00:00', '2018-08-13 01:00:00', '2018-08-13 02:00:00', '2018-08-13 03:00:00', '2018-08-20 01:00:00', '2018-08-20 02:00:00', '2018-08-20 03:00:00']}

Problem

How can I fill the calendarydays field with the function output for a user to select from a dropdown, and where should I implement this logic (in my view or the serializer)? My main point of confusion is that, because my function depends on data from trainer_availability_only, I don't want to create a separate model/table for this information (as that would seem too repetitive). I also don't fully understand where in my serializers or views I can implement some sort of dropdown for a User to choose a single calendarydays value for (like I would be able to for a ForeignKey or OneToOneField for example).

Details for the other models aren't really relevant to the question, except trainer_availability_only, which basically gives the user a dropdown selection that would look like this:

('Monday','12:00 am - 1:00 am') ('Wednesday','4:00 pm - 5:00 pm') etc. 

Any help is greatly appreciated.

2 Answers

Answers 1

Well I don't have the exact answer for your question since I don't understand what you're trying to do, but this is what I can tell you about implementing logic on models:

Assuming you have this model:

class MyModel(models.Model):     field1=...     field2=...      def foo(self):         ### Put your logic here ###         self.field1 = self.field2 + 5      def save(self, *args, **kwargs):         self.foo()         return super(MyModel, self).save(*args, **kwargs)    

This method runs whenever your data gets saved (or updated I assume). Don't forget to validate your data before saving and don't put your logic on views or serializers, those are not good ideas. You can also create another module for your logic, but that's more complicated!

Answers 2

Unfortunately, with the built-in template engine for Django you cannot dynamically rerender parts of a template based on something from the backend. As I see it, you have two options:

  1. Request new options via an API in your application and update the HTML using JavaScript; or
  2. Generate all possible sets of options for the initial request and send them all at once and then just filter in the template

Option 1: in the template

First you would need to make an API endpoint for the Availability model. It can be as simple as a single url route with a pattern like '/availability/(?P<pk>[0-9]+)$' (ex: '/availability/5'). This route should return as a JSON object the Availability object with pk=pk (5 in the example above). You can JSON serialize the object yourself if it is fairly simple, just convert it to a list or dictionary that contains simple Python types (e.g. str, int, bool, list, dict). If this is going to be a larger application with more involved models or a few more API routes, it's worth checking out Django Rest Framework for building your API.

With that route working, you can call it with JavaScript from your template. You'll likely want to add an eventListener on change to the <select> or <input> for trainer_availability_only. So if the user changes the value of trainer_availability_only, your template with make a request to your API route for the Availability object specified in the form.

With the options returned by your API (calOptions in the example below), you can populate the <option>s in the <select> for calendarydays. You can do something like

const calSelect = document.querySelector('#select-calendarydays'); calSelect.options = []; // to clear the old options calOptions.forEach(calOption => {     const optionElement = document.createElement('option');     optionElement.text = calOption.text;     optionElement.value = calOption.value;     calSelect.add(optionElement); } 

The text attribute is what displays to the user and the value attribute is what actually gets submitted. Note that the above supposes that calOptions is something of the form:

[     {'text': 'Monday 12:00 am - 1:00 am', 'value': '2018-07-23 00:00:00'},     {'text': 'Tuesday 8:00 am - 9:00 am', 'value': '2018-07-24 08:00:00'} ] 

And that'll do it for this route.

Option 2: in the view

You can also do this in the view, but it isn't particularly scalable. What you would do is load all of the Availability objects in the view and then pass a dictionary containing sets of options (presumably one set per Availability object) and pass that to the template via the context argument.

That would look something like this:

option_sets = {} for availability in Availability.objects.all():     option_sets[availability.pk] = availability.get_calendarydays_options()  ...  context = {'option_sets': option_sets} return render(request, 'my_app/form-page.html', context) 

Where availability.get_calendarydays_options returns a list of options like calOptions in the previous option.

Then in the template you need to get the option_set from option_sets that is for the selected Availability object. Do do that see this answer: Django template how to look up a dictionary value with a variable because dictionary access doesn't work the same in the template engine as it does in Python. For populating the <option>s in the <select>, you can do something like:

<select id='select-calendarydays>'     {% for cal_option in option_set %}         <option value="{{ cal_option.value }}">{{ cal_option.text }}</option>     {% endfor %} </select> 

Why I say this is not very scalable is because it requires sending all of the possible sets of options for calendarydays in the initial response. I recommend using Option 1, but the choice is yours.

This seems like a complex problem so I doubt this response will answer everything, but hopefully it sets you in the right direction a bit. Let me know if you have questions.

Read More

Saturday, July 28, 2018

django rest single thread / blocking view

Leave a Comment

Is it possible to block a code in view from execution by all users accessing the view while it is being executed by a single user? Kind of single-thread view.

I need it because i generate the python executable with pyinstaller in this view and passing a username into the executable through the config file.

For example:

class CliConfig(APIView):      def get(self, request, format=None):         try:             config['DEFAULT']['username'] = request.user              #make a build with pyinstaller             bin_file = open(*generated filepath *, 'rb')             response = Response(FileWrapper(bin_file), content_type='application/octet-stream')             response['Content-Disposition'] = 'attachment; filename="%s"' % '*filename*'             return response         finally:             config['DEFAULT']['username'] = '' 

So, basically what i want is to generate a python executable which will have a unique username it it's settings, in django rest framwork APIView. I don't see other approach except passing the username through the settings file. If there is a way - would appreciate an advise.

python 3.6.5, djangorestframework==3.8.2, pyinstaller==3.3.1

3 Answers

Answers 1

Why do you store the username in the config? Shouldn't this generation be per user?

Anyhow it is not good practice to do time-consuming tasks inside views.
Use Celery for long-term tasks that will generate executables and will accept any variables without stopping Django. At the end of this task Celery can send executable to email or something.

from celery import Celery  app = Celery('hello', broker='amqp://guest@localhost//')   @app.task def generate_executable(username):     # make a build with pyinstaller with username      bin_file = open(*generated filepath *, 'rb')     response = Response(FileWrapper(bin_file), content_type='application/octet-stream')     response['Content-Disposition'] = 'attachment; filename="%s"' % '*filename*'      # send email and/or returns as task result      return response   class CliConfig(APIView):      def get(self, request, format=None):         task = generate_executable(request.user)         task.delay()          return Response({"status": "started", "task_id": task.task_id}) 

Answers 2

Take a look at Django-channels project. channels give abstraction to developer and support many protocols including HTTP. You can rewrite critical pages to channels consumers. So you will be able to write asynchronious code in block manner using async/await constructions. https://channels.readthedocs.io/en/latest/topics/consumers.html#asynchttpconsumer

Answers 3

This is not Django related question. What you would like is to lock the 'method' and not allow the other threads to access it while there is a lock on this method. This is somenthing that python can do for you.

I recommend you reading this post http://effbot.org/zone/thread-synchronization.htm#locks or refer to this answer Locking a method in Python?

Read More

Tuesday, June 12, 2018

Displaying Django subcategories in category and products in each category json as Json Child

Leave a Comment

Hi in my Django oscar project which Implements Django oscar. I am able to implement my custom API which I use to view categories and display them. The issue with the API now is that subcategories of a category appear in my API view as categories and I would like them to be in an array indicating that they are subcategories. My categories code is as follows

customapi serializer class

class CategorySerializer(serializers.ModelSerializer):     class Meta:         model = Category         fields = ('id', 'numchild', 'name', 'description', 'image', 'slug') 

Views

class CategoryList(generics.ListAPIView):     queryset = Category.objects.all()     serializer_class = CategorySerializer   class CategoryDetail(generics.RetrieveAPIView):     queryset = Category.objects.all()     serializer_class = CategorySerializer 

customapi/urls.py

url(r'^caty/$', CategoryList.as_view(), name='category-list'), url(r'^caty/(?P<category_slug>[\w-]+(/[\w-]+)*)_(?P<pk>\d+)/$',         CategoryDetail.as_view(), name='category'), 

Json

[     {         "id": 2,         "path": "0001",         "depth": 1,         "numchild": 4,         "name": "Clothes",         "description": "<p>Beautiful Clothes</p>",         "image": null,         "slug": "clothes"     },     {         "id": 8,         "path": "00010001",         "depth": 2,         "numchild": 0,         "name": "c",         "description": "",         "image": null,         "slug": "c"     },     {         "id": 7,         "path": "00010002",         "depth": 2,         "numchild": 0,         "name": "b",         "description": "",         "image": null,         "slug": "b"     },     {         "id": 6,         "path": "00010003",         "depth": 2,         "numchild": 0,         "name": "a",         "description": "",         "image": null,         "slug": "a"     },     {         "id": 5,         "path": "00010004",         "depth": 2,         "numchild": 0,         "name": "MsWears",         "description": "",         "image": null,         "slug": "mswears"     },] 

notice the numchild is 4 for the first which signifies it is the parent category and the rest are the subcategories.

The subcategories are rendered like this from the Django-oscar model

class AbstractCategory(MP_Node):     """     A product category. Merely used for navigational purposes; has no effects on business logic.      Uses Django-treebeard.     """     name = models.CharField(_('Name'), max_length=255, db_index=True)     description = models.TextField(_('Description'), blank=True)     image = models.ImageField(_('Image'), upload_to='categories', blank=True,                               null=True, max_length=255)     slug = SlugField(_('Slug'), max_length=255, db_index=True)      _slug_separator = '/'     _full_name_separator = ' > '      def __str__(self):         return self.full_name      @property     def full_name(self):         """         Returns a string representation of the category and it's ancestors,         e.g. 'Books > Non-fiction > Essential programming'.          It's rarely used in Oscar's codebase, but used to be stored as a         CharField and is hence kept for backward compatibility. It's also sufficiently useful to keep around.         """         names = [category.name for category in self.get_ancestors_and_self()]         return self._full_name_separator.join(names)      @property     def full_slug(self):         """         Returns a string of this category's slug concatenated with the slugs         of it's ancestors, e.g. 'books/non-fiction/essential-programming'.          Oscar used to store this as in the 'slug' model field, but this field         has been re-purposed to only store this category's slug and to not         include it's ancestors' slugs.         """         slugs = [category.slug for category in self.get_ancestors_and_self()]         return self._slug_separator.join(slugs)      def generate_slug(self):         """         Generates a slug for a category. This makes no attempt at generating a unique slug.         """         return slugify(self.name)      def ensure_slug_uniqueness(self):         """         Ensures that the category's slug is unique amongst its siblings.         This is inefficient and probably not thread-safe.         """         unique_slug = self.slug         siblings = self.get_siblings().exclude(pk=self.pk)         next_num = 2         while siblings.filter(slug=unique_slug).exists():             unique_slug = '{slug}_{end}'.format(slug=self.slug, end=next_num)             next_num += 1          if unique_slug != self.slug:             self.slug = unique_slug             self.save()      def save(self, *args, **kwargs):         """         Oscar traditionally auto-generated slugs from names. As that is often convenient, we still do so if a slug is not supplied through other means. If you want to control slug creation, just create instances with a slug already set, or expose a field on the appropriate forms.         """         if self.slug:             # Slug was supplied. Hands off!             super(AbstractCategory, self).save(*args, **kwargs)         else:             self.slug = self.generate_slug()             super(AbstractCategory, self).save(*args, **kwargs)             # We auto-generated a slug, so we need to make sure that it's             # unique. As we need to be able to inspect the category's siblings             # for that, we need to wait until the instance is saved. We             # update the slug and save again if necessary.             self.ensure_slug_uniqueness()      def get_ancestors_and_self(self):         """         Gets ancestors and includes itself. Use treebeard's get_ancestors         if you don't want to include the category itself. It's a separate function as it's commonly used in templates.         """         return list(self.get_ancestors()) + [self]      def get_descendants_and_self(self):         """         Gets descendants and includes itself. Use treebeard's get_descendants         if you don't want to include the category itself. It's a separate function as it's commonly used in templates.         """         return list(self.get_descendants()) + [self]      def get_absolute_url(self):         """         Our URL scheme means we have to look up the category's ancestors. As that is a bit more expensive, we cache the generated URL. That is         safe even for a stale cache, as the default implementation of         ProductCategoryView does the lookup via primary key anyway. But if you change that logic, you'll have to reconsider the caching approach.         """         current_locale = get_language()         cache_key = 'CATEGORY_URL_%s_%s' % (current_locale, self.pk)         url = cache.get(cache_key)         if not url:             url = reverse(                 'catalogue:category',                 kwargs={'category_slug': self.full_slug, 'pk': self.pk})             cache.set(cache_key, url)         return url      class Meta:         abstract = True         app_label = 'catalogue'         ordering = ['path']         verbose_name = _('Category')         verbose_name_plural = _('Categories')      def has_children(self):         return self.get_num_children() > 0      def get_num_children(self):         return self.get_children().count()  

when a category is selected, the corresponding JSON so look like this

 {         "url": "http://127.0.0.1:8000/nativapi/products/16/",         "id": 16,         "title": "Deall",         "images": [],         "price": {             "currency": "NGN",             "excl_tax": "1000.00",             "incl_tax": "1000.00",             "tax": "0.00"         },         "availability": "http://127.0.0.1:8000/nativapi/products/16/availability/"     },     {         "url": "http://127.0.0.1:8000/nativapi/products/13/",         "id": 13,         "title": "ada",         "images": [             {                 "id": 8,                 "original": "http://127.0.0.1:8000/media/images/products/2018/05/f3.jpg",                 "caption": "",                 "display_order": 0,                 "date_created": "2018-05-26T17:24:34.762848Z",                 "product": 13             },] 

this means that only the products under that category are returned. and if a category has a number if a child, the number of the child should be returned in as an object Array.

2 Answers

Answers 1

I would suggest keeping category specific data separate (in details page) and only having a products API.

For getting products under a certain category, you could do something like -

views.py

from django.shortcuts import get_object_or_404 from oscar.core.loading import get_model from rest_framework import generics from oscarapi.serializers import ProductsSerializer   Category = get_model('catalogue', 'Category') Product = get_model('catalogue', 'Product')   class CategoryProductsView(generics.ListAPIView):     serializer_class = ProductsSerializer      def get_queryset(self):         cat_id = self.kwargs.get('pk', None)         if cat_id is not None:             category = get_object_or_404(Category, id=cat_id)             return Product.objects.filter(                 categories__path__startswith=category.path).all()         else:             return Product.objects.none() 

urls.py

from views import CategoryProductsView  urlpatterns = [     ...     url(r'^caty/(?P<pk>[0-9]+)/products/$', CategoryProducts.as_view(), name='category-products'),     ... ] 

Since we are using categories__path__startswith we'd get all products under that category, including those under the subcategory of given category, and so on.

Update

As for the subcategories you want listed, you could simply add a SerializerMethodField() to do that for you. I'd suggest getting a list of ids for the subcategories so that further fetching the details of that subcategory would be easier given it's id (simple lookup from the existing list of categories)

serializers.py

from oscarapi.utils import OscarModelSerializer from rest_framework import serializers   class CategorySerializer(OscarModelSerializer):     subcategories = serializers.SerializerMethodField()      class Meta:         model = Category         fields = ('id', 'numchild', 'name', 'description', 'image', 'slug',                   'path', 'depth', 'subcategories')      def get_subcategories(self, obj):         return Category.objects.filter(path__startswith=obj.path,                                        depth=obj.depth+1                               ).values_list('id', flat=True) 

sample output

"results": [     {         "id": 1,         "numchild": 1,         "name": "Cat1",         "description": "",         "image": "http://localhost:8001/media/categories/images/categories/cat1.jpg",         "slug": "cat1",         "path": "0001",         "depth": 1,         "subcategories": [             2         ]     },     {         "id": 2,         "numchild": 0,         "name": "SubCat1",         "description": "",         "image": null,         "slug": "subcat1",         "path": "00010001",         "depth": 2,         "subcategories": [         ]     }, ] 

Answers 2

django-oscar uses django-treebeard for a materialized path implementation, pretty much the opposite of the nested hierarchy you want to retrieve.

I have no experience in writing serializers along with treebeard, but i am pretty sure that you will need to rewrite your Serializer to something like

# Get all categories from /caty class CategorySerializer(serializers.ModelSerializer):     children = serializers.SerializerMethodField('get_children')      def get_children(self, obj):         if obj.numchild == 0:             return None         # Use treebeards built-in tree generation         [CategorySerializer(child) for child in Category.get_tree(obj)]      class Meta:         model = Category 

Notice that i have NOT tested any of this, i am just trying to point you in a direction that might bring you closer to a solution.

Read More

Wednesday, June 6, 2018

saving Base64ImageField Type using Django Rest saves it as Raw image. How do I convert it to a normal image

Leave a Comment

I have 5 image fields in my model , imageA, imageB, imageC, imageD and imageE I am trying to save the images in the following manner.The image are of type Base64ImageField

    images=["imageA","imageB","imageC","imageD","imageE"]     for field in images:         if field in serializer.validated_data:             content = serializer.validated_data[field]             dict = {field : content}             modelJob.objects.filter(id=modjob.id).update(**dict) 

In the above code content contains the raw data.I am trying to update the image using the dict I created (the key is the field name and value is the content).

However the images saved in the imageField of the model are raw and not an actual image. How can I fix this ? This is what my serializer looks like

class Serializer_Custom_RX(serializers.ModelSerializer):     imageA = Base64ImageField(max_length=None, use_url=True, )     imageB = Base64ImageField(max_length=None, use_url=True, )     imageC = Base64ImageField(max_length=None, use_url=True, )     imageD = Base64ImageField(max_length=None, use_url=True, )     class Meta:         model = modelTest         fields = [                   'title',                   'zip',                   'imageA','imageB','imageC','imageD',                   ] 

More info:

If I do something like this

modelJob.instance.imageA.save(content=content,name="image.jpeg") 

it works fine and the problem is solved.However there are two problems with this approach first of all I do not know the extension. How do I extract an extension ? I am just guessing a jpeg here and it works. The next thing is Ill have to check for imageA,B,C,D and E if they exist and then save each one individually. If I could come up with a dynamic solution close to something that I have that would work as well. This is what my jsondata looks like that I am posting

{     "title" : "Some Title",     "zip":12345,     "imageA":"/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAkGBxMTEhUUEhMWFhUXGSIbGBgYGSIgHhogIB8fHSAbHyAeICghHR8lHh0dITElJSsrLi4uICAzODMsNygtLisBCgoKDg0OGxAQGy0lICUtLS01LS8tLS0tLS8vLy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLf/AABEIAKgBLAMBIgACEQEDEQH/xAAbAAACAgMBAAAAAAAAAAAAAAAFBgMEAAIHAf/EADwQAAIBAgUDAwMCBAUCBwEBAAECEQMhAAQSMUEFIlETYXEGMoFCkSOhscEUUmLR8AfhJDNygpKi8RUW/8QAGQEAAwEBAQAAAAAAAAAAAAAAAQIDAAQF/8QAKBEAAgICAgIBBAEFAAAAAAAAAAECEQMhEjEiQVEEEzJhcUKBwdHw/9oADAMBAAIRAxEAPwC10fKegPUMh3+2BqKCJ52m972w+fTvUlfV3FhOxMx8YWaJIzDgwQd0m0C/7if64vZlUUGpT0KQL8Dnf/X7nxGPPcnys7Z7CH1NQpMHVUU6khxsY3EWsd8IuWrNRZxZi4KMpiG8WNjYH8xghlyldalSlUd2UxFSwVTsw0m48RbC/UsX1Gb2YWmDc3uJ/vgtuwJVou1nsGCyx2PiI43M/wBce066OTqp7D/0n538yb4HZctLkkaI7R4O9hxMb/0wY6bkmYo02aQGv4k/NhthWjNhDJZoemEV27hctHP9WwVyKqFdG9TS0T9rLwJaQNM2tP74nz2XSlTRjEOZUEC20T5P45383M71DKVaYp1FTu4J0jY9wNtvxikFXYrS9HLetV0p16i0bqjSNaxt7GIIuLjCzmKAaTMGPx+/7YaeqZPLlajtWVai1AtHTOhkJAjvY6QsEySfuwrdQ+4B2LU1kB0XeBtc+Y52M4pCK9EJE/Ri5qotMeoSdOgsAGJBG/ncg74r5unoaoEBAV4IPBHFzNpO/GMrZV0KvTqIZAgo8FSwJ03iCIIPE49oytN1qKbkFSN7iDPlb/gnDuhCFAWJt3cjn8fi+PM47HR2kAEyZsYA2H/N8Xummiz6XJpyLNEydgA0iJtci2+BvUax0vTazBhHkGRMmbiJwIq5BapWePVD1AYBUWX3tvf5xvUo6WYfaJuJ3I9/GIek10DEkSBMA34sDbz7YJU6IqK7s3eSCsT3AkhgALDcG/g4aehKJGRlUKArK5Ugi5SCbHx8bxGL6y5EHTLElWspbURMfqHif9Xm46rUYawAIRo1Akd24Hsf1WP9MG6WUaqpiqbHspsSSFdtRj9tRP5xCTDV6JadAaS5YBid3HPtxc8fN8Tq4EdywePaQTjXN5JyxQNIDkAntDeGvtMgwb3xrmlAEXOntnYWH9Qecc6v2FaHHI9WT/ClUsywDYzbvBmIB7YF53OKebyLVFatT0SxsQD2wDsPeALmfa84B5LMXA0EaWBm0sRI7f54P0+rlaZQlNKkMH5B+DzYX9hbDfcvse7BiUKiUz65Ukccrs3wOR+MD6NXtYtZCSQs/Ij422GJs51QEM6hrRdo3Mi+504DZdxp0yZNoJ28D++HgmxWFlrCmqxIB4O4PjyYGD+RTLhA9VHAA+5RGpjBi4mY3i0YWaaGleoNQ0xMWDMmoDcEEE+eMFeo9Eq0FDsLNe6xBsYiSbExeJiR7O0Esf4tdbekpF+1b2B8zbycGQJC7dwhjaAfnfbf84g6H0IVqSE3LEkMGEDbtIknYG8DcjBTPUwqhQrKBuTtPgSZ21D+2J8H2FWMfTunrRE7+/zvgd9QV0dRpuwuLce3nAtM3VJRiFYG0Hdm4JA333O2M6g1VtdSoYCWOmP25uLH84ZztUkODaOZJYldybx+3zgn05ADrYLB2B5/bY4FdAz3pu4YAaxK7TBG9z5U+x+ME8zXAcJfSswQVZoUXsDG5/l84jVbDRLT6kbovdB2W8/j84H9Y60EGgfxKkwACCQdxqjb84p0emGozS5gkwdgb7E8RGLWU6ZTpj1GQSZBgQXAsPxMx+T8nXsYoDL+p/ErVDNm0iNNjIvETtv44xH6qPUBQFoEWn7m4E/GDlSiBSBKDQplr7c8/nziL6XeoU9VV0amY6YsVmFk8jSJ+TgX8meyxkOj1H0vUICkCeCB7HYHYH84Jv0+mhim9SPYjfnkYmyWfJU01l3Ahi2w/bc/GLGX6NSCjXJY3JkxPtGwxWCtaFsU+tdNanUFZgSCY7vcW29/+HFbMZJ6pCvURlqRMdpBEQNxJNx/ub4ZKvUFq0l9XukTpU3BkATHGq174g6hlmp04CLpIuSPzB8H8/GM1RZMW+oZCllpqIppuFkKrQCCRud7kftOAZy/rkmVmRq1Pc6yJ0gi8GbDjDnlehJWV2WsKhgRqaSoAEhhuNzEAR+cImfoOhB/hj+LC2MSYmVMEQbf0nBUfkZV0XM9lwqJSaxu7kGe07D3mC2DOV6zTRFFN2ci5HpwJjTYgwCRO37Yp5fJElTUjQe2dMwrfcLXB0gDbnG/UloCi70GhZ0gxJgbzedz+374BOfehhyHVDmhTpvlwVBhWkgWA7QY+7bke+IfqijkUqilVpVFLQTUUwqEmBM2XYxMKb4r/wDTTqzMtSnIJDQFYwD2r3TBm07f5cKX1Zk5qF0zNJtYYWqEdpBUySgUKLiQt5jycWSEboEZ+jRp5h0ql29NyG7RNjaIJWTcRt84kzr5R1C0j6J00yTVUmXAggRqCgkAz7XtgNVqVlUCpTJaoQdUfxGEnaLiYMEiTB9xh3X/AAmW1rmsuzMhZSyNrlWAh2LE3LWAKrtsZknjXZPsX6OdpoqNUpLUI1KEYNThpnWHSQ122gmQZAscQ9Xzb1yaroE4sukuOWImJFpKgDbaYwS6lTyYoEgmnmEP8JRcQSZmTfVJYkbGBA2wp16lV7sWZfMm3t/L+XthbT6Eeg3kM9QQ6EoK0ndgSxI2P7iSNrjaIwJ67kof7VVrSVICmVdm5tdSADc4iWuwUNJDKLECCQbGTza2NK1IuVjUWb7hYQTCi5Pud8HGmpWZytUUshAaWuPGCT1H74Kl2k8CDabARH4vgTQWbyT4wVylEz23I3mQIIEjeIk74pk+RaLVHMtpBZdS0zJWODBJJXa/PE++CmW6hSKsF1jUjaGBknYAMCYXYgkYqtSDU2J7KYEVGmLkjtAnuje3G4GK9BaQVmolmYHcrZRJMzPHwZ9sc7jaNsYsvnyKKoaKvCEGGIi4XXcROp4I32mBGIUpApuzNq7gBaLmVi+/Hv8At5TzdY6noI6h0PqED4ViRGlRMjtHJngiSgpIY0yU0Htk/ggmN7zF9sSmgkdZ/wCF3iGtLSYW8wR54n58jHtM1BDKAwJsCdrxYXAncDEtWCupwDNitxO0nx7/AL4JAK1JtCQqgAgAmR5iRO+/zsCcS5UwqOyo1NQSzAemwliTMRsTpv7wBjSrS0qqU4fT3Fyv3NJYRNyCo8Da++I1qEJJUarCNluD+LHztbEheo2gBT2rfSOBLaj7RN/GLReg0WaOVdEGk9weYixIIMAXn4wVyD1ivptWADsJSowMtNt5taZmLYEZWqx5Ibhr298XsilNSQErOJUswNzG+5HBt+ThIy8thRHmPUytXR6hQ7tp3M90Ha0wYwXb6lr19FNwAkyWCnibz8gi3vghnsiMzlxVFNAdQCnkkwtwBBsY+VFjiCrkqrZxEaopD6jTdgDFMlpBHkNZbCAfIxVuloLB+Vzy06ppVZ9XULHlbbFRK/n/AHxTzdWqaqOjduomouwa0/aAIOqL843+scktOoHK/wAJYV6lEyY911EgC4uR773nr5ta1YOqkkhQBI7jwJEAzta2BNcFaBbI6pBFNx94cppA/Qb6idjcD8E4ZKWXlEYAsxXumwQ7GwI4HPkGL4i6f1qnRoaGogy5VlUkksW5Ym8gbbCw2jG/R0d30uTpCEtTMwYggSOLg+8DG0+iiZBk65WmNChgCeJnuncC4+N78YuUT6oDvJ5t7cRsI3+QMS/4x6dIIqwANIflRsABG9v5HA/p+YXSJaLQYv8A2xCbppBs3+oXpnKkBdJfUiTdmc2GxgKBeSfFsbU816aAGfT2EDmw2/AjjFAVErVrM0U5HcIEmRIm4gf1OCVWkR+eZ/pPP/fBk20jInyOcQWQE8/HH/y84nOfqNcOR7Abfsd8DMtS0jUSd57fHFhOLbVK36FQD/W0H/6gjE+Ul7GSJaGTVczrpOHp1AI0wNidQMfduCPj2wb69mHp0iUUHhp4HmOcLn0r1qszilmUCVVMMJEOYs6gjtnxa/nDcgBJYgi0X8f2/wDzHoIEntWJ+Q6YiacxTlGZrlmJvEHtiwN7fHGA31DkHfMIrwzltY8HkaQ3nwd4GOlNSG2kaf6EbY5tm8s1d/WfuplriYIEwNMbb7YEkPGXJnn1CxSlQoq7LqYhgiydIljFyA0wOcVMx0ug1EoMxTFQD7qpixBkEj/MYUC4F/yS+pOgui03p12ChY9OpTkyYG8R+/i2KGW+hatOvOZak9NlJcE3MyABIjUCQf6HGjGts0qo550/ONTqEByobcLvF5CyDAgkT74em+mstRyvqlg9SokK1wFWqpUzLEFhI+299gLgawWnWq6DT9OqZYKsimASAsBZUySbHbziXN1CtSk1OorK1RToCwoYSdMMZUC5k/6vyryJMkol7r9CpkWo0gUzLQfv1MQxhUhSSAwtEW7RbeU/q/U67sDUN2/8w2AqXLS4FiQbCRIAHthnfrdPuerFV/S0EtDk6maHQ8ECIi8QThSpZudRZNZNlXnSRE3iYMWtf4wryOQstlSox0E6Z0mNQBvc3mNgYF/AxA9fTBE694IBX9vzzIucMWZRKNNKbhTJuxTbZhIsbgtBM2b912pk4EhrNMSJ0j+1j/3wYtexOLR4xeoJhT3Fm02UTEiAPjaMUqxYSpCwQDpAusQLmJvExJ34k4OdIphNJ9Q6T96rvuTBDWMge48+4WvVmq7zvBJ8QQJxSE020B6R7kmAWSB/scO2Q6IuYyqVQzKWcUiQxqMAEP6baZKwAeCCPGE7LLTLFiNa6WkASftYAx/6oM8b4az1J1yVCEUCqyyw7ZKgTJIAmDc3+4zFpabs0QZQ6dSerUpu0Uhq7zsCDCnfnxz/ADBDJ+lqamtQFDUbQP0lQrAPoKbN9okgyRIgYHU8oxRzAtA3+4GBa8xB32tjbptErVVmH8NWHqEgFY8GODf9rXxFzYOjoFPPirl6NBKQV1gFyEAMm7KCDqldyRvxhRrpUo1KyatUObqZEFoFwYFr/NsGOn9XFSguXYHUoY06hgrTjuYqAmpmgkW/0xcWBZTNQ7bPYhdW3JkjyRx5xOe0M9hLpOU9ZgrkC8CT9xNh/OPHF8MVbp7qqKEEqwBIaffTYkCbX+Ywo0M1LBVCrKhDyZ1TqHj/AC8298MvSOq6UamCIK6hAJuPtAgCA2mNW0D4B5q3Ro0bZvpTQFKlT3ElSDYQb9xCwvHvzxeFKgaNLLKyq5bVUaJOiDCsVkntiwsLYg+q8ylbLUKi6QzCG0m5iQV/lIneRih0/rjfwUTQSisssO6++ok8yQOd/NumlEbSKlWkdbelJG/MAC0zxf8ArjdOrGkskAFWF9Jn8gbjnGz9MYV2pkim+zD5vHIAji5xLnMkKP8ACZBcwDFzbYGTO87e+I+N0MloPVOrv6CFK9GG3oiFj92B+R84AjpzFldayu7OAaeqzCZAt3wdrHc3BuMTJWq6dS0KkCEjQwJJMSNRj8+2I831OmSKbUSjqO4MCZA+BpIm84eMpLaDQ1VuorVy1fLvl/SCgrUWBpUxIJItEwfOxPjCR0bMlaOlVA0tqmYbSuqw4BGg/NrYOZTptTMIDRqEgkqUOqGAknVNgomPz5OLFHqgoVKlJwESsJkqSoZVAYi0wUB/eeZxe3JbFo1q1EJorSUIQhJabsTa83uCeBvbbDF12itNabTvAf39j+P6DCFRK+uwsxDaEvsJsDsBbyLeAduiLkUYMgDs6JAZmGlTAhQNR8XwsE02FaBGbzYNJqevTqhmBWNTbGN4EgEX5O2AnpMZRSFYCSWN7nxuf6/2q5rMO19LNUa3F4i3tEDicMWR6dRWi5dhqN27+YsFi/P+4wkk5y2D2Xeh5Ck4ADtq58mLkzeJPi5wQ/8A4jXlu0bTzO9uJOK+RpJRZIkBhpA7p1DiIOoni/GPOqZ2ojkBamjy03neIEAf8tiqiuOxl3oG5mkKbb2i4Bvb9XMj5xZymWNRdRdrngTHtjRGcghKekEkkg7/AMjx74GVqRUwao87/wC+OOaSZTsI5bpaVgJp/wARJEEmFsIZQTqUCRY2jbDlSQ6RO4G/98LXQMwwqnVABG7N9wvB/wCeMFOssNBbUokRBNmPi34g47McvCxJW3Qt/VP1K6qyagLdwUwY5OrgWMfOFzpnVPUqCmlVQgI1iDJ0i8MRO0i2388S5+alWVVSB2nVAHJgwZJJOm3I2xJkqI9OrWRANbLTRAV7VUQW7ohS2pvMH2wNtfsslSNus9YLOKOXZqjWI/VeNzN2IHmd8D8/ns/6BZ2ZVqWBcwJF5F4F+T8Yu9HyQGXfM1aNM0gxZgSQxgkKosRBax+N98D/AKo+tPUHp0GdUcKCh0mL30m4utvIMEHjDUKwn0H6VLUabVaBZ45IF4PdBEExIMXvcjmh1fJPQAB9NCC2qVBLWIANjvq98Vl+r836nbmKoAG5AKgeCYuR/bArqFWs2qpUqAyWbWPuve3I8+BhJJCfIMVNbggiQSQLSW3AI2Mkx4/pgv0daeXT1KihnAB0lbkOZEOCRe5A0+b2JAjLZNgrlHWwgKDeeOLf2wYWmzoLk10B0lhF2kQNV97Enk25xOfwJBWDc9Xo9wqUnTlO4MASYloveAv4POKOXpAvpXaDqMEge94gEwt+cW+ruwqLpem5ouVDAWZZJK/Aab7/AN9OoqrE1FkU6g1KrAAAkghQ22xP998UpJUGS3ZOvTqlMqAumQAXgsApDG7QOFJhfG4wq9drj1qiUwoRZUaeQCL/ADI+bnDr9N18qVqLXqOGIKhVLHsANjpAgBokAiRN+Cm9cCl3alelLAEJAnQLTJ0nsspJIAmb4f6debsnLoj6NVVQSw1cEAxYggGb8mduB+CVAOyFiGKK2mSDpBidM7aiB7G3OF/Ig92kn7TIHwf5AxODtLMtSpqDcVR3wDsCDH/q2vFpHjFM0dkhl6B1Vg1Bq8sihkoKVBWbySJU/qF+4zG0DBnpXSqLLWNFqL1lqaApZdDIFV2Iv2sdL3sPuiIst9OABZ3pI1Ihvu41doi+8GRvcD8G/p/og9HMEVHdKcOAEI1HSQJk2MyP7wZxDkm6KGvSKiUVqVPTSodRVFLFYGoKxSCS0qbXmCSAbyHNUEMoB0z90EkRJsTt74zMiKSIrBTL+pctEFdNokfqHvzGBlKuyMFBEjkWkHjwRtOIpNgGLo/+DWk5rM4cEenG7QrA2jkkAg7gG/OI2zut0BOofawY30klt5OmZgwLSd742+l8rTrsVYqH1Aoalwxn7GYXAni2okXEXMZX6KKZinSq1wsmCEOogwSCTErJFp+7jDOLlHRqfordHltVBlSzFg5PeIK6gv8AmaAI/wDd+LnSMqEzFNlUu51htQmyvpPaLkgG8/PnFfpOV1NVVRBsdf8AlBtrlmAhrQT5F9ySP0j1PTmxKux/iaTM2Okyb7D+s40XY8aSLH1B0KscwShClou+xtHiQSV+AIviTK505av/AOIZKjLGphc8EQTcC9o3wy9WqpUqJ6hIOhpAntEq3Bu1jbj+eFGn1iiKrF6T1aYtLLMb+x3Mm3tiOXsel2E+s/UFOsQKSkW0kmbbmPmSD++BGVymiKlclCCYCnvncAztY77RO+C308gdiVCLSP3F1tEz+82HgCMUuqZGnSao1OsXSI7iIkzYWuMBN1ZqAuRyzio7pWakFEk6iSWNwI94MEadt9sS5b6gh6buDrp1FZiTA02RoF7EEGfAgzAxDk8zT9VQw002fuFyCIi95mw+MMPV8nTqxTSnpKAhX3UaiINtoliZ972x0Kb0agH6/wD4011AX1XkTpO3cGJ2JhgLbaQLRjolPotJwhNR3d4JJNos0QIjYfyxy8150aluGZZAOqVDWgnaYvvhny/1JVCUVU2juPIEjs9pvBnzhudS2bj8Fn6soquZsDpqNEgSNRkET/6iJuB3XxQy00nXulGmAwiSu9p8HmJvFsXurvUzGWFYgJ6LNGhpUBWuszqBAHjjAapmxVfUzLDXMcQIgEkgA3J+TieaS9B7Q7UHo+mzApqA1LN42+3wfgRi7Q616q6RRLsPukgAe99pF8KOQzyA6tEQ33ARPBvzG0f/AJizV6vDMtFCEbeFBE8MSVsfeMDHntbNxLy5fMs4g00WT2oe6AJuSCf2EeMD85llDkq1JQbwKc/1af3xX6nmAGIqEyPuuTEfpuIvzbAuk9bMTUpoCpNrm3tthZNvoZJjx0kKqCnVTUAbFhBaeSOTePycZ1hVFKrVjSKaksdMkxMCCDJO0HfED9ZouTDKxW0+5IjfxH88Lv1V1I1CFUgdwlgTDRFo2kHGxzbfBhStisc7Xo1lqep/5k6UMgEMSDtF9VwCD5nHQ8wKVLp4AAmoIUWN9iQYvYki3jCtlen06mbouEnQQ5BIWAB2mZteD+2GDM5xK2bSk7TSpjVIExaZPO+n98dSyJjvsO5vpRrUBRRl9MoEaJUzBnzG/jnHOOu/TtUPTRlARFBmAQJlQJMG4RrE8Tzhy/8A9JUy9JpUOJhWdomJubeByf5Yi6TmqdcVMzUU1GckNDSNABCgKLjUR2mPzc40ZRltE9+wHW6bROWqM2um9JQJGnQwMWWbzB+b4Weo9OZYaGAqLqUsV2mJPN7xG/8AR6+t81RrUwnpvTqipA1A6TaCbSpgEe4thZ6x0rL0UWoKtSoSpCqTC7j7fENf3gTG+NKhHsTWzDUwYbuJMrffzx7j5jBrpvU1VgXqXIBhEbUDYEcyogkRG+B+ZybF9dNWKiDIHBgR7mSf24wSyeVkU0oVC1YyVQRJIWe0A/dvv4JxNpMVJos5n6MJfWzEpVnSIiDJYBZ/Vptpid/gCuqdIzOXGmmHajUIVR/mnaB5DDgYgzvUKr6OxlZZPsWt3SSWJtNyYkgADBVfqnOGhRDmmFRnVGQw02JZ5DaQZEEAcnBUZe2M2gP05kRvVqgvUYxpEFoK3Yi8DgzxO2BfUa7tRp0yVKqzMzKsSzAgEgARvH7YK5vOa2DMP4mmTWg3UTIAVgCSTo1GMLOYzjkLJIIJJIYmZMnf3knzOK41u0RbKmSqlSSDFo3iZsR72OGXKh6iq5SVXSvbAJ3O5kSYMb2GwwB6Zky7i4CkwSfB/vxgwrlf4SEQDFjvBNxJgzh87VgGj6cVShqMoLMG0wyjQRLEaWPfKrtFjG0zitWz9RHq5fLAiidMlbgydxYSGYAAmwgWGBXTcv6gMN6ZOoAMbHiCZEc8cjFzLJmaNZvTkkAFiokaZ0yCeAYE7bc45Vq0FMO9A+m/UqNQrUwZBJZTBUiIIJggHnzxbdXzmWJzboJKpUcUyJAKq5AKnke+Gf6X6mn+Po1HAp02qHXMae5GAWIsJIvEfGBXSeosczU1ICqu/pqDOmKh+2N7dvuI9sUjKo8ki643dG3TpFZk7ZWGLPEydxC2bu2iPc2OOhdMdKlGmioAQA7UxSV4O7BnqEyRcjTp0iBfhIzXTPXqujKEckFiikWvb3Jj9484ccp9M0xTQatDkdxVSQ9MzAJYW5MiCJAJuJ5nl5fiS96CnR6lE9SZKasAcvpKssGzsQo9gIifbC/kso1LqDpTpsQuvWgjuQhAYkXt/QYj65mGyWbyrq2sSyF9V21QQD+rSoO5Y7tsFxvnMzUXqOaIc0mVFOsCTDMswBcyARI/BxaM/HaLRd2Xer9ZWUp0KTb6SDPYTMiRAMpxM7HEGapLl9RK3IJF+086IP7fvc4r0n0o+ts4lQ30mnKvIkFf4dy0Te4g/OF7rHVy40mpWdiAw1KsLO0ACZv/AMtjmpzF6Q49F6ktSn6isylTqYLTMeSJ2Ei8TPjF76nyVKpSGYQakK/oAvquGbnc8H5xzzL1axplStUIT3NcAFYsQt7CN7Xw4dG6PVI9R3LUnBEEsQZ2MCy7cm+MvHRgBXyNPSFpgFjHfP2ze0Ryf5n82crk61Kq1MMaqKsOJgpqBkGbExAIE7e8GbqHRUpHVoRzuACb2tAG0GP+84Zui9CFUI7rIt8sbb8AAW3nGWR3SKca2xP6g05kLSMK3eu0llUhvhtO49jtjJdoFF4ZpILD7SQRM/Emb8YPfW3TFywR6ACmmy1L2DNqgi+7aJsDwPaA+TEfxBp9NXBkNsNW41GTBg/jFMhtPaGLLdZSl/C9MFQipqBgEQVluNm8nCalP06zKzEMjgK0RKydJ+DvPEYZaPT/AFHj0yG0DtkCIeBqj9rHj4wM+tcn/ESooEooDngLuPaAZ3/zb4SD3TH4l+lXBTUCWgSR29pHB1EyNuR/vTq5lqs06jKCxDCGCkAA/cf6CL7wcLvSeqVXMJYNIKmLgcDVyQT5/OGs9PFFNXp0lX7g4KkzwWY74nJfbdM3YK6hXRTTpktVabARtckmwsPPxvbFv/H1aYAQogiSrXv8hsLWV6xorvmCVZ4KU4UQw5JFxExfwLb48fIGqddQuGO9vztIA+BbFqa7MnQ21ulVKR1TsSjCLLpnTcG4mL7mSOIxT6YHr129RQwpKzEDawO1xMsbSZwT+oeprSoltNPU8hmMTJk6o+W98AunZkU6TNbUSDP+kbTf/N/XDYZ8o8q/QIqtjT0DKkpUrzBmASDACwW5O4tvxgb0/raZelUzLq/dUAAVfHeRMgDZRvijm+v1Uyi0kVVRt2m8OCdiN5BHttfAPN1qyCmSwKGWCXOklSJIjcAT7EA+MNw7fz/gL6YZzn1AMyKulUR2WArtuWOmwjcWt/th5+i+m1suNFZFCr2yPNoF7ke/mccq6J0cvm0os6hEHqM5sCPA8m+8C6nxjuPT+ntA1k6f0qbxuSTN7zttGKwxxjqP8km9AvrnRqeYzSqSFCpNSIBNzaeJkYG/WnR2GVqfxgaZ0hF0r2yY+7e/Mm+5wFrrm/Xq1lKOUYqNYGlypso1WELfzbCrn81WqaGrOx9VS2n/ACBCQRoBhb3X2HvhrXwB60TUHoUwKavUBQg6tpcHdQCSoN/ew22xLTmmZorFSQVBBLQJsAfAJNxGxNhgFQDF20ldMkrIJcCPtEC5m0xz7YY+h5tddT1W0LTpEwzRYr20wW3YP233H7YjNO1RorQO6ZQOZinTQ1lHcFRWLguSxUk9qgXOqRxGBGbyjLVdXWoml2lF1MqARAlyBAuN5+bDBrKdRpUmKU2ei6xpaZt/lkQSxJOwjfHv1Fk/XBq1T3t3s6szdkCBBMKDqAvtER5WLXVCtcgNn85TfLQLaILA7k3UaCBa1rkXI3nCzT0FH1kqQJHMmYvIvwbX/Y4I9QpqEBDD/SZM2Jv/AKbgbD+pwJqZBgqsysA0xtBgwb/PtjoxJJbJN7DP05QRSGc9hqJB3Ki5LAEeBgzkqD06XrKikUzOoiSwZ4iI9x8YD5CpFBTEMlQD3NmsTsdxt7WHNvJZ6uPVSmkgyxK3UAXbzYCDa8R5xOdttButE9KgrgD/AMtadR9TKAD39yg1DAaNJhBHP4sZB6aEqxJpup1kEkwDO2oGZE78zxYFlnI1g2IYFl32J4JubxHzgx030aFZmzEsPTfSAfM6SQNhBO0xO2Fk30FS0b5CrTILgxDyD+qL3E8gc+YM426VR01dagKTJksYmSSTzcmce9I6VRqK9QEqWb+GRMDtZlH6jEgDk298UnrdumBcHVLRwd+d8TbkpaG8k0Hj1JRmzqEhiD2MFK2+4GStze5t+cN1fMAij6gYtURlQGqwFQCCAQhhDq2MGxHnCRlOnp6lAMwdXCxYq+khiI94sZuDO4iSNGhXpvUNDX2htIZSbMADcmAVB1SDMAW3GElFqwNPdhb/AKj5ZWy1LNJIKVVBIBvIIkFiSRMRM74vmgBn6dQ1HX1sssVGYTIJOqYIgyLQLYpfWRb/APlimyx6kEAH7WRw7agVWJA8bn84odYzoanlNmf/AArIBE3BtMf+mwxSErjT72Wg1VHQ8xUUiK5Y6lgPCwRvvBiZH/BOOVv09zUOkF4Z1kCWhDEwBeRsR7+MEst9b5r0TTNNO/V3sIJ4MTYkcDjxiH6QrN/jGOpg06vcLF1gRIJI328jEre7EtdBTL0dLIaupWIClSJuNJHEzp/74dsl00KXMQr3K2Cz/mgefcnCz1U0/T1ikKb0yKhUCLzFiRuJ/rPOL9Cq2YX1BVenIAGxURebrB3jHN9xRdjpA3rDmktSWJCg6WFpPAE8j298E+kZhqlAA6VJF7d4HJhQBttPtecUeo5ikgKu+vUJWVBnSSSTAAHsffG3090gk1alWCQbDTCAMtoH2i8jnbDpOaKSeifP5damX0CkWB5YkAQTJvubxz84V/p/NUhTqZZ1IIOiWiwiwBPIF/O8ecdJq0VEWkFd9xJ5HF7Y5H9YZdaWcYup9GsCYk2Zf1CDIuNQt8Ypjg7cJMVO0N30vX0sUV7BtM8nSY38MIf/AN3EHBXrnT6dSjVpNZGRkJmSLbzzpIF8KtLL1KGlnHeqio6rESoBN+dSz+QPODWZzRrUw7ApTjUF/wAx4JI322B8b4WcWtrsbs5l9N6pem7QVaN4GofFzMR72wQ6lmgU9FET1WJCdtwDuNR4kE/udgSKP1hlRTzKvfTXXvDbBv7yI38HBHprI1NauYqLrPYQ7yywYlRM2YfbsFEDacds6aUw66C/TPpKmKaMtR1JUKxIFvIESIHj23xH/gtNtagjeTz7arxi3ks9TpsIbUosQG2Ox7kb5OzcbYvZzP0ma6MYEDWsnzuabEi/nHFKTTtmoW+t5hqtRFDSWi35gG2wO/4G2LXW6ChUTUAgA1Gdhafm0fy8k4BZasBUeoqsQCQkm97Ak3O39cDOq13AMQYbumZbifj/ALY7oxqooKVDP9X9UWu9MKBoUAidieAI3EfG52wGHUX76xqmnWmy01PcDM939jgd0vKCKru8KFkIzQCSDzDC0QBF8R5YdhcszQYAtY/vPkgfnDqCiqJtMlyNdmadbALK3uRc2jULSTI+bY7NlfqygMu5p5kMyUz/AAyYcPsN5MTeBOObdK6JSHpslRXMEGmCAxdRwwPLbWFjM4LfWlagqp6NNleqwYyzENIib21S0GLjnAeTypCL9jD9O9fVqSUlpPUqSzuFUAbwj6nI2OnYjkXjCb1aq4eolSmfUJfWsgCxYkqQIAIJMXMjfFrp3WK1FQoqxTa2oGNIm8SduCRtHzgQ/UlYHsYksWep5BNze9xANxx8YSM227Fk0yPKUvVpgqwVDJpBvu+4jTqi4sPk284srWq1KdR7EUw2qXOupIiJJkt3avhbztjdshI9HYmWVgsBQvBmbknZbH2jE9fpiDLIZ0IulmJYQzlwpa1xA8HwPfCyezKWqF+tS0MYcsSt42k3gedM/vOL9HOirT9OolR1gl2V40mCNWiwfu0n+d7DE2eYV66sKhqGZZiAvZFtK6dQkkwTMyvM4myVcUzTVmquqAkqB9hMsKcm5YI0nzeIvAUvZoe7A+ao0qOR9QoPULaNUm48gSQGgHxucLVbqrMKc39MEKrXAEkxfcEnnBD6mzDVIJY6VMAH99/O2/t7YXRjswRTjbJzab0MvSHaqwQkKmoNIEBJ5ECwsBtgr0nO+jqFRS9PUwAtfSeBMXkn34OFfKZgDTdR5Jk+bmAT4wW6eT6kqwZjJP6geftMC/HvHzieWO3fRuRZyGXIrmpT1607oQkR8FYPtFr4jrsRWARVggBeRBAuZG45McHGlPNN67EkksVc6fdZMxFryRa4GLfozmKYatIZW11E0ibGBdRpNr6t/acSap7foDVljrko1elRYiijKDcE2ESdNhLHgnc8WwIeiRBOwFo25wTyoK0sygiokAkwVYhTZ7lo4MX/ABvgflidIkgNbbxcX8i398a9aDItjXCtMBI0+bk3/f8AqIx1PpOb9ZxlyfVosodC9PUxlZZdYeCygN3MvG208qdqbpYkFZ08CZEj4jxF4w6/SnUDSMNWFMssTVZtGmYQ0+3SCq9psIFhycSk12Lytm/T+lVKmWzZdI0a0W7AsQglmk3BKgHSFAkWvgF0HNAtlXYEgKgudtTlT+DOGSj1t6j16QKOapKwft7SYsVNjsNhJF+cJPTguioht6agd3MOBptyJJkft52KSabrZbHKKHvMdTpPQGVCglSRrcGE7/u7SWKkfn+WBf0p05BnabWKnUZiNmMWb20n2HxgTnsq4ZyIkGTLcXIk+bG1jtbEj1qtBaQZLJULaSNiVEwx7tLDTbawg74hFMEXb2db6zkUqUypEiINtv25+MA/8JCJTUEgT9rFQ0R27AYk6r1iocuDTpxWIkVSSEprvqLGRJ+0DyRxgLlaGaDL6eZlS0wRMqRdgIEx7725xDJhi5cvRRM26jRJqUsxEhDpZSSQQTeZiYMXPAJ4xJ03rNUVFo0wS9lZZG6tqIkzDRq38jfFqpl8z6TIQrBhAIGgp4OnYnncXG+FLNVKlJmchg+kFiASdSkKTcXkaf3xfDpUUWzp5zTOgRx6XBCGSNVgAdrgzPEYTf8Aqn0gtQLUkI9PvmRIAEFfMRfBPonV/UKKtgoO27nYSDtYsTjzrtSpVEqFNMKxczeCYAvbg+9vzgPL5K/QtUwLlMw2YyVHMISNO4n730liCbQIXTtcx4wWyyKqwizTpgOgNiwe9MC0wIg+Co42RforPVKPr5cSTTfWqzuBeItMj+pweyVUlgA+gMzKZiyknSDvEMSvi+OiaSbSHKH1zQFahp0aakygjhdU+87iY2I9oTOkdRj7izNI0RteS02JiwM/OOj57LilUC1GA1d5MBpGjTbYkCDA98czzSihmiywaZJcDjS09p/2xT6aXKLixRhoVqpUVBS0Ce1gTMb7H9xyRgtQzLEblveB/tiajRpogV1poGJ0u0auLtFwDNiYjFLOpSptpD0m5s7ACeLCD5n3xFvk6SHTKdAJ6BCatZglvtVdywM7kALf59sRJTFPp7VGUFq1XQpO+lbsf/lIn4wSzmfU6kVixjTBAESfEgC8jAnP1kXRTYErTH27/dduRtxisctvaE+4gh0esuYo/wCDpd2ZdvUg7GD9snwokxgFUyij0w9TTqdlMAsCqz3z+oahG2CnVerf+HY0yELMArBiCAYm6tsCo8mfxFDKdLpGvVQPrppTARtUq1RgDvI0gxMbDnDqSpyF5X0HvpXpdKqyCoXVXYqjCD3R+LyRtYxfGZqnTqdTpZenWapRy/arMQSLlmAZREAnT7RGwxR+lulM1RQKj019MVajKSOxlB0TFmKzz741+jO+rWzBETdLCFJMgQLEAACBx4wFrlKzJLsK/U+YFGqaVAgHVOpTfuAAQCLGLyPG2FxUM3XUoWCLHVIG3gxG174m627ai7adRIkLuIt+xOKhotKOWAkyA/3Fd7FrSTcg2iI8YWEdEJHuazEDYxMgSbe3gi+JMsoqU9LExrVRGotcj7REC3n3xrqW47y19QWIYeBuAfwcedAyxBNVxKoZP+mxhgPmL/GDWgqLvYdzZOXytIoykGoyspXdgxUXiYMDb2PjFKnVrim0vb1A8hpDNDAFW3kCQRbjE+cy1TRMMVIaQNpuwNz3bQTx84H5gEBWRlGsWUEkr28sCAYBECJ7rjCKOilUti11gy+kRpQAQIAMbkCf58+22A4wb6ijSSzCQCxGkktaxkDYn/MRH9QuPRw/ic3smyVKWuCR7fGGTo2TBZ1M6ASJiCLMY39hv5wE6TV0kysgm58Dk+9uMM/086h6iLJUKb+wQAki03O/tzvied9opjim9kuYU0cyBRIYtS0wVHMrp99ok74q5tHpwhJU1AC42FmMGdRBsJ43I5MzVK5OYDqoAQBRaREmN9+Tix9R10FdGpoqEMdfYSN/8uxF9R5uNoxyR7SYzrpA+lXZS6iSI0mNyDxB+MQVnAklFIBAPuTJuLcD+nnG3ql0gafuPfMMfaJ28TexvxiDOU4e1+1CSbQfb82/Jw6irYnC0Gs1SUUgVBCG+wImYuw2twfa18R6ggC1HaooH2hp/nJifI/ngm7r/hCSIcppkH70AnR5OwMkR28RaHKdMWplyWqaKaqurSs2LwLiCzB4seLcYg6XdglCmFPpjJPUqKSCEtpNM6W3lu6LnchTsOI2G1coEr5mmpNneYNnW7SY3BW5O3PGGX6Pol29Onl0qFZao3qlVWwABEH1Lyw2MW4vRyVKOp5qhMgKacmIh1IkBtgNX/198LCDkNCPLQLpu4puoDMqhVYkA9rXXVzOokA3nBLrvUjmaDD0wrUgHYg3BB+4WEyJn/hwvdNrlaKmoYDMh02OtY3A5KlDv5w9Z3p9OhS1uyansPTaRVQjkBYXgwST840lxYy06B+ez9WF/iOlN0XUAR3NaDLGJEeZtsYwWoZ2okQPVCoIl4hL6BILIS0zxGmNhOKPQFNTJ+lUK6gWgKQQRpFxx90zyIONvprLmrlSrV6utGI9JPIMHcgWiePxhIU5NIrq7Qcpdf1qFNGuraRNpjULe8bf7YTfqqppJ0uWD9piJXX27weYN8T9Sy+aZC1IMlNwoful20EoZ9yAD22I5OBWay5pqahqgSQChIvcgGBcEASdsGMeMxk6GLo1KnoRrlghmRs2oAd0j5k/GCtPp+mnXVnkEiRBkjTOm/leI8YWfohhmcw9EI6gqSe7SSFg6ttp4P8AscNOT6fUUA1KbEanCF2hhA0ie4D7QNv3wmbG0rA5W6OefUaPlc3TrAA6wA8jtJn+h/thgo0i1JKhGlHlLSPuYhTYcMBfzHvIz63yJIKyWIBZSReVibjcXtc/PmX6O6mlfJnLVXAbV2XIeTBkGYkRYf8A7jpjcsafxoMWEeuZlKlFWLBKjfwiWU2ZXCvBHs7G+8CMIvUuklkqPuUu3sJ06drkWJOwHzhtFOm1WijSJCs67EVEJptDR2lhFxJm/sIvqeqqCFQpTUENpMlxsd/bk+BhlLhJJGf7KX011JWpqrGDcMRYng6iDcHG9bICqxLCoYOkFFJBA9/MzOAXR6qUjI831c2jYRb2+MHsj1TQgBNzc3U3Jk7mecLl8ZNxMpFbOktUUVANbku4ufeDO0AHbgjFeooqS7LG5kHnkf8AbFatUD1ahB0fpCk3F9J8jzzzjajTZEQST6rHYcC3Bna5kbHDcKRJm+XrD1AmsIhQk+otmj9IgHckX9j8EfkVdVeqVimZKkg6ZJIULN7gEQfF9sGhR1d9FkLtIUzFtm1KZtEj3vvGM6qCAiNTX0kKk6Z0tsAI/SPut5ODGaWqAmi7XpmhkKtSTqdUXVO5caWgzYBTA8BTPEy/TVAplgxsHk/1W0ewB/OAPX86xy9GhadbPAH+Uemo8yTrNrGRGG6l0zSFQWKqEkT+kBZ38rP5ONk1j322UfQq9UoBKgBfVIkEiNidr7R5i/nfFqjnFdlbRqSkDqBb7gRA+L4G9YzCNXYjUVUkTvIFp/YE8/jG+UpXYBNjqJIkAKRv8krbB4+KsS/RnUcyGiGPZYTwNhG0DbA85mo+mnqIUmDH6bCSTYwSOdp3GJaz++oERbknzyIufxaZxplXLBUVIYHuJJOsE2kcRtb2w8dIWeRobs1SSmopiXV47QZAEiw/9ptNwJJHOAvWh/HehSJCaiABwQAhNh4AHJwT6pUBqUFAGoUhqEkjUZHtMKI5nzgPJSq72FvMRuDH4xCMtX7KTkuKBn1FkynqB3ZzTZVVimkEMoaJt3XuCO2B5MAaQJIAvJ2wU6znjVE8u5c2sd73E2mPG2BdFiGB8Xtj0cV8dnKEMpRkmLBTJj2NycNfSM6r5quxplAyrpU3MQtyQLzZiffnC507OFWJ3kGPaZI3+TbBbobilXLC0CDIM3AIgfEG28Y58j7sticU0WOrZk+pSqppLFzUZVjSG1AhPEqN+O4YJf8AUOokUygEmpJaIjUpbTxzJ2/tin9X06VMUBRYNrL1GvsW0iBYEAaTvN8DOuUZpwpJ0lT7nUCJA523FtsRircWF2maZGifTpuwZVYsNZ+1oJEfiBcY261VD1HYzTJAaEJICGLD9/jE2VIbI0gNXbVlpPJ1bX2943J+cDutytVp2NOBxa9v3GKRjcmw8fGw902kBQ19zIKY1Dj1ChWR7AlTG1hIw4/RldDkYZdNMamdiVGrUBK7aogCRNz7YQ+i58ek4vZGIHBNjf8A+Me2+Gf6V6Oc1Q/8300UFWp6yADurb8CRJH9MRzJK2x5KOmuwx9M9OepVrJlqzUMuCCWn+I4ZYU3sqzyRJA/OBWfyHpdQqowKBqagESxO66pMyZvJOC1Lri5enTIX1q1UST+oatQQsJH6VUEWkA7YCdbr1DnVNSslV1pgsqLAQgzAgkbnVY4MWrpGxryAKErToKN6TuuoWkLqWP/ALfiPfHSfo8FqDwNKKoKpVe5IlifAMXsBx745tR0o9QsO2nmiSCdwZJEHgxvjoucpZcpUyyB6VVe+mkhUY6bLpmbowvuYmbW2RK9iy7A/QHZK2bpDTqSszb7q0PIi57Sdp3GJ8jXrClmKdJaQFOox1VGkrs57ANoqbyCb8YDdPr1MpnFkFmqUhzuVlAPaBpgn2wQzvW6lPNVWVBpdEaon6pgob82VQcQkvLl8oaT6YU6r0+syr/iMyxBcFNI0BiAJBAH2mFi5O9zNlXPfTiCo8iFciCkKL73SAfB5uZx0DolcV8pSYlZFmn2lbAzvHt7YXPqGto9NCthO5IYi3zeAOBONLI1kq+w0uxZ6lkHpaTr9QLdzOx1FGXy4BVTOxDD3w+ZPrYcFnpoagEMwkBYJYdukgW5HjCr1FaAijTfSDUB1dxDhwyTxCyKIiNw2GjoWVbM5EKwU1Kb94Y6WAUBACVG2kAWO2LtckZ9A/q9N6i1WdHsLIWEpJGoxzvhF+ls1/hs7DwEJMk+D+qT4N/xhz6v0t6JV1QGmwDrZrRwNY1GLbxPjCN9SK7VhUnc+RNze3t/LAxNNuD9mSoN0KtVw7NOssatG0TpYswBPkczbBPMZxCmtQrUqgDxp7iCI2iBF7De+84ioVUahT7x2NMkgKZPd/UfyxWoZioVrUt9Pegj9JnUDEgQZI+PjCSXJfwNVoqUumIv2KqRABgkx/cx5k74np5ymkqrGxvqW84DrUY1GTVukgHg288+3vgfSzzgXYTztb29/nFFib7Y0UkaU8yAhGgNJmTIInneP3wayXVWRSophxTplAT+kG7R+APiPfAPK0HLgUgdS9x2gQffweMWFBpowJu8XHj/AIcUmk9HPvsv0K9MFDTUaqjnsDMWTmZ4BmYk2mcFZQZbWmoVKesuh1QyiTqm+mQAd/E8wv8AT6Oqoukxa1+Taf5xglmsvArBNkVQCf1G5aAd2KlbfjnEZJN0aMSj0Cmcxm6ICkgODpmbKdemTxNvzh6+pmrUKBcOoAQCLTqJuRMk7xY/0wp/9OHUV2eoANKkEMSIkg6hySIwQ+v+sCqiIgH3FtQ/UAO0eeZ8WGHybyKAzegJ9OqdVR2+2mhJm0FiF23NptizU6yAlZFEgtCHkj35tAn53wP6ctQUzUVSAD3H9PsL788HbGy5fSQ3cdY+3ZgJg7zeSSLccYdq2xLd6K6sCIEgW/f++5tgz0LKoIZy8QVJkNveYIsPa/zijlKt6ihtJViApHcSYEkixAAnFwsyB6gpfcCZpiwgGTtZYBN4thZp1SEit7Jep5pVrO6aiVAWTETB2jcQbGBfi2AGez06oi+/kQI3/czjMxmGeRO7T4BO1/xgfnKLaB2k334/5thoY1asabtFOpW4WyxEfmf9sR0x7fGN6lTvkxtG2PAf9Nid+PjxjuWkRCOVIOlSwAF5ieP+fucX8jXP+IJTkDiZsAZ83m38+cDcsovPiJAkA+PMRf8A/MEcuyiqAoA7ACo+L7zIMzjlmlsfGgr1oJ6dIx/E9aCxP6dB7QNokT54wM6mYHqKzakKhSeLW+IvvO0YtZrPdyI3cobUg2AJ3/lfEHVUUZUy0MQNKyIY67sQDeADE7YljtcUUlskq9RDZZVuHLBqnaNIuSNMbDuMg84HdcMm5nSpHx3GP64uZf06k06YaCAtIMRJbtN//tzz7YjzWTLHSWWyaZjYg/z8XjFYtRezR/FlDpVRRck7XHngj9sOH0l1N0XUEUgNB4IMWPk8/sMLeU6YsMNRkW7pFzxHMH/hwa6RRKUnipwDCmdXBBHi9weMT+ocZJmd6Oj1MqalFmNUNpUEMYLDSWM/Bnm/GFD6mzmqoGGif8O5LoILkbavwAMHPozOrodSbaLxsnBEHfeRxhN+oX01WXcem4HxMjxsMcX09/dSY8JbTIatXU+YlSob0qh5MWDGRbgm5wzdD60Eq05DEps0AA2KCZI2kfgcRhXNQVGcqfuymn3LKCymPcAftiak6ovaDNVImY03BiPwMdWeN0HJpWXvq3PerUpZkWZappkTFipKmOCNBH7Y96wzU2oNCkOrBj5J7wOSIUW35xR61Po1EQHQiipEWUqwO97xPO2B1fqJdaYaewqQdrbfyDG+EjDlGL+LRO24nSf+n+aH+GqU0ntM91yJiRtgL1J5qOGlmKsotJPudzNv5Y1+laFWjmA6uppVFlr7yNQJ/wBQ2xbz6UlzJZp1cMH2mYsQZIBnE5pVaLRVxsDdXrFqCNUYEp2Bl/y9rLI/SBKm3IwT6P1bM6f4NXS1RBUEEBWKk61OqIA/tgf6wVKlJxIKshPkoSFYgf6SAD5vij0vN1UplkKhqZFdQRNiCGQe+oX4viuP2ghnL9TqMo1B7WIAJIEE+DaMCvquqmaRSqKjKgXY9xBBmeSVJ/8Aw4Lvm6tUCpSJpSsF11SwOykBogFSBPj3wD6rnalIS7amAAsQeSADG/59sSx6na7ASfR/VKZDUaigrU7BsAAbMSPgg/gYuUKj5dhUPdpLUqomdSbE2N/0sPacKGRrtRq2kQ025/7YZ89X1sVEKtQKCJnbdreVEW8Y6sip2NdKwXXRSwqKxnXJLC+ltubwsfOPepBg500Uqar6im/HBjjBUZRalEqCFIMGd5402iMWUzFJRpY3FrWH9ccss+9KznnPYv8ATqbaNSISpaJBN/IBiNgBtv5xTzKBWvItceRvH8h+2MxmOpfnRRLVkmWGkmAe7giwAGN89mB6QpoSbmTeL7mDtP8A2tj3GYKVsLikiSnUIoqWbucteIJWyyI8nXM+BitkumvXqenSEyDoDcgSd7Dgj/bGYzBerZKthPO5ZqWWRNGnlmVtiwlQ17Hxgb1bqzd+m4YINRMt2fqn9JmRbicZjMbGk+xpaNenVvUpS9RixJHxICxPwZG0Hg74OVc0yU5IBLCCDKkL9uqVaxlJsPneBmMxsi8gLYK6jklpABCCYBJ5DXESN+D+2KFWqyXWVU2YgwYjz4nxjMZgRe9hl7AVYyx+cYpg3mOYxmMx3HOE6qaSfThltf5499/wcT1M7qdREEKs8z2hf7Y9xmOfimUx+i31SrqXLkfcraSfIuQfO0Yo1FBpVYVTcS3Ihj42kcm1vjGYzA6X/fJWS2XukOnpuI7tJ0kzJJ/ltIxUzWr0tRMgk33mf9749xmJt1P+/wDsSTrRZ+mVDMskwpE/3jBLoEK9QP8AaWgAi0yJnxbGYzC/ULTRRKooZugVESqVpvMIe42Fu7TJB4i/zGFTrVao9bU5JlHgxbwY+LfFsZjMQwKshoLoh6QoFPLPAP30z54iPxq/ng39PVKJksGDiFlTAIEWsPbGYzFvql4s2T8UM9LoPr0aoQEI6sLmQpg2BFxuDB845p03IvVohqaMzoDq8KsbmeQQf3xmMxLD4Y21+hYx8Q50Oq4NNwwAMNpYxIncCL2Jv7DFv6ncNW21KpUm/tt7b8YzGYVdsbG7iyhWrNDFQdWrUSPG554XV+wxX6bnR6iyAqxpaQD2vIJv/qiRtjMZiuNJoawglSqilAzEUyQVB22KsFkwCsG35xQzrBxpYR3C/LR88W2EYzGYX+qxn0DurUdK038uwJtMAJA/mcEMjmgS2knUtMhSdzYGLbxP8sZjMWa5Q2Izc5liQ0drD2uY8jbFLMZ0qYifyMZjMShBOVUc0ls//9k=" } 

2 Answers

Answers 1

The images encoding should come from the client, this is how you can know which format of each one has. Example:

base_64 = "data:image/gif;base64,R0lGODlhPQBEAPeoAJosM//AwO/AwHVYZ/z595kzAP/s7P+goOXMv8+fhw/v739......

You will receive it from the client and you know that is a .gif

Once you have validated the extensions and the base64 you can convert it to images and save it in your OS:

Convert string in base64 to image and save on filesystem in Python or Decoding base64 from POST to use in PIL

Once you have the images in your OS, you can link them to your ImageField in the model changing the name property: Set Django's FileField to an existing file

I hope that is clear and helpful!!

Answers 2

Short answer is :

import imghdr extension = imghdr.what(file_name, decoded_file) 

ref : https://docs.python.org/2/library/imghdr.html OR https://docs.python.org/3/library/imghdr.html

Basically import imghdr is the key in function Base64ImageField.get_file_extension to get / extract the extension of the function.

With below class extend / code you don't need to do modelJob.instance.imageA.save(content=content,name="image.jpeg")

You need to add this class in your codebase to call or for trial purpose you can add in same Serializer class file itself.

from django.core.files.base import ContentFile import base64 import six import uuid  class Base64ImageField(serializers.ImageField):     """     A Django REST framework field for handling image-uploads through raw post data.     It uses base64 for encoding and decoding the contents of the file.      Heavily based on     https://github.com/tomchristie/django-rest-framework/pull/1268      Updated for Django REST framework 3.     """      def to_internal_value(self, data):                         # Check if this is a base64 string         if isinstance(data, six.string_types):             # Check if the base64 string is in the "data:" format             if 'data:' in data and ';base64,' in data:                 # Break out the header from the base64 content                 header, data = data.split(';base64,')              # Try to decode the file. Return validation error if it fails.             try:                 decoded_file = base64.b64decode(data)             except TypeError:                 self.fail('invalid_image')              # Generate file name:             file_name = str(uuid.uuid4())[:12] # 12 characters are more than enough.             # Get the file name extension:             file_extension = self.get_file_extension(file_name, decoded_file)              complete_file_name = "%s.%s" % (file_name, file_extension, )              data = ContentFile(decoded_file, name=complete_file_name)          return super(Base64ImageField, self).to_internal_value(data)      def get_file_extension(self, file_name, decoded_file):         import imghdr          extension = imghdr.what(file_name, decoded_file)         extension = "jpg" if extension == "jpeg" else extension          return extension 

One more information is you can have Base64ImageField( max_length=None, use_url=True, required=False, allow_null=True, allow_empty_file=True ) these params in case you want to make this optional.

NOTE :: I had got this code from StackOverflow only, but not remembered from where I got this I had liked this answer too.

Read More

Friday, April 13, 2018

How to POST Model with Many to Many through in Django REST

Leave a Comment

I have a model with a many to many connection. I would like to make this model available in Django REST. By default such a model is read only, but I would also like to write. Furthermore, it would be great to get the information of the through connection integrated into the GET as a nested model.

... class KeyDateCase(models.Model):     ...     diagnoses_all_icd_10 = models.ManyToManyField(         'ICD10', through='CaseICD10Connection') ...  class CaseICD10Connection(models.Model):     case = models.ForeignKey('KeyDateCase', on_delete=models.CASCADE)     icd_10 = models.ForeignKey('ICD10', on_delete=models.CASCADE)     is_primary = models.BooleanField(default = False)     certainty = models.CharField(         max_length=1,         choices=CERTAINTY_CHOICES,         default='G',     )  class ICD10(models.Model):      primary_key_number = models.CharField(max_length=10, primary_key=True)      star_key_number = models.CharField(max_length=10, blank=True, null=True)      additional_key_number = models.CharField(         max_length=10, blank=True, null=True)      preferred_short_description = models.CharField(max_length=128, ) ...  class KeyDateCaseViewSet(viewsets.ModelViewSet):     ???  class KeyDateCaseSerializer(serializers.ModelSerializer):     ??? 

How can I achieve this? What should my view and serializer look like?

2 Answers

Answers 1

Normally I workaround by indirect way by POST to through table and implement nested-create(). Please provide me more information if my answer is inaccurate.

models.py

from django.db import models   class ICD10(models.Model):     primary_key_number = models.CharField(max_length=10, primary_key=True)     star_key_number = models.CharField(max_length=10, blank=True, null=True)     additional_key_number = models.CharField(max_length=10, blank=True, null=True)     preferred_short_description = models.CharField(max_length=128, )      def __str__(self):         return f'{self.primary_key_number} {self.star_key_number}'   class CaseICD10Connection(models.Model):     case = models.ForeignKey('KeyDateCase', related_name='connections', related_query_name='key_date_cases', on_delete=models.CASCADE)     icd_10 = models.ForeignKey('ICD10', related_name='connections', related_query_name='icd_10s', on_delete=models.CASCADE)     is_primary = models.BooleanField(default=False)     certainty = models.CharField(max_length=1, default='G', )   class KeyDateCase(models.Model):     name = models.CharField(max_length=20)     diagnose_all_icd_10 = models.ManyToManyField(ICD10, related_name='icd10s', related_query_name='icd10s',                                                  through=CaseICD10Connection) 

serializers.py

from rest_framework import serializers  from keydatecases.models import KeyDateCase, ICD10, CaseICD10Connection   class KeyDateCaseSerializer(serializers.ModelSerializer):     class Meta:         model = KeyDateCase         fields = [             'id',             'name',             'diagnose_all_icd_10',         ]         read_only_fields = ['id', 'diagnose_all_icd_10']   class ICD10Serializer(serializers.ModelSerializer):     class Meta:         model = ICD10         fields = [             'primary_key_number',             'star_key_number',             'additional_key_number',             'preferred_short_description',         ]   class CaseICD10ConnectionSerializer(serializers.ModelSerializer):     case = KeyDateCaseSerializer()     icd_10 = ICD10Serializer()      class Meta:         model = CaseICD10Connection         fields = [             'case',             'icd_10',             'is_primary',             'certainty',         ]      def create(self, validated_data) -> CaseICD10Connection:         # import ipdb;         # ipdb.set_trace()         # create key_date_case         key_date_case = KeyDateCase.objects.create(**validated_data.get('case'))          # create icd10         icd10 = ICD10.objects.create(**validated_data.get('icd_10'))          # create connection         conn = CaseICD10Connection.objects.create(             case=key_date_case, icd_10=icd10, is_primary=validated_data.get('is_primary'),             certainty=validated_data.get('certainty')         )         return conn 

viewsets.py

from rest_framework import viewsets  from keydatecases.api.serializers import CaseICD10ConnectionSerializer from keydatecases.models import CaseICD10Connection   class CaseICD10ConnectionViewSet(viewsets.ModelViewSet):     permission_classes = ()     queryset = CaseICD10Connection.objects.all()     serializer_class = CaseICD10ConnectionSerializer 

My Repository:
I share my repository with many questions. Please do not mind it.
https://github.com/elcolie/tryDj2

Answers 2

In regards to creating or updating nested objects, the documentation actually has a great example. I would provide you a better one if I could. If there is anything confusing in the example, happy to explain it here.

If you follow this approach, your GET requests will expand the nested objects automatically for you.

Read More

Saturday, February 24, 2018

How to incorporate data from two distinct sources (that don't have a RDBMS relationship) in a single serializer?

Leave a Comment

I'm trying to serialize some objects whose data is stored in 2 databases, linked by common UUIDs. The second database DB2 stores personal data, so it is run as a segregated microservice to comply with various privacy laws. I receive the data as a decoded list of dicts (rather than an actual queryset of model instances). How can I adapt the ModelSerializer to serialize this data?

Here's a minimal example of interacting with DB2 to get the personal data:

# returns a list of dict objects, approx representing PersonalData.__dict__ # `custom_filter` is a wrapper for the Microservice API using `requests` personal_data = Microservice.objects.custom_filter(uuid__in=uuids) 

And here's a minimal way of serializing it, including the date of birth:

class PersonalDataSerializer(serializers.Serializer):     uuid = serializers.UUIDField() # common UUID in DB1 and DB2     dob = serializers.DateField() # personal, so can't be stored in DB1 

In my application, I need to serialize the Person queryset, and related personal_data, into one JSON array.

class PersonSerializer(serializers.ModelSerializer):     dob = serializers.SerializerMethodField()     # can't use RelatedField for `dob` because the relationship isn't     # codified in the RDBMS, due to it being a separate Microservice.      class Meta:         model = Person         # A Person object has `uuid` and `date_joined` fields.         # The `dob` comes from the personal_data, fetched from the Microservice         fields = ('uuid', 'date_joined', 'dob',)      def get_dob(self):         raise NotImplementedError # for the moment 

I don't know if there's a nice DRF way to link the two. I definitely don't want to be sending (potentially thousands of) individual requests to the microservice by including a single request in get_dob. The actual view just looks like this:

class PersonList(generics.ListAPIView):     model = Person     serializer_class = PersonSerializer      def get_queryset(self):         self.kwargs.get('some_filter_criteria')         return Person.objects.filter(some_filter_criteria) 

Where should the logic go to link the microservice data into the serializer, and what should it look like?

2 Answers

Answers 1

Because you want to only hit your database one time, a good way to add your extra data to your queryset is by adding a custom version of ListModelMixin to your ViewSet that includes extra context:

class PersonList(generics.ListAPIView):     ...      def list(self, request, *args, **kwargs):         queryset = self.filter_queryset(self.get_queryset())         # Pseudo-code for filtering, adjust to work for your use case         filter_criteria = self.kwargs.get('some_filter_criteria')         personal_data = Microservice.objects.custom_filter(filter_criteria)          page = self.paginate_queryset(queryset)         if page is not None:             serializer = self.get_serializer(                 page,                  many=True,                  context={'personal_data': personal_data}             )             return self.get_paginated_response(serializer.data)          serializer = self.get_serializer(             queryset,              many=True,              context={'personal_data': personal_data}         )         return Response(serializer.data) 

Then, access the extra context in your serializer by overriding the to_representation method:

def to_representation(self, instance):     """Add `personal_data` to the object from the Microservice"""     ret = super().to_representation(instance)     personal_data = self.context['personal_data']     ret['personal_data'] = personal_data[instance.uuid]     return ret 

Answers 2

I suggest you to override the serializer and your list method.

Serializer:

class PersonSerializer(models.Serializer):     personal_data = serializers.DictField()      class Meta:         model = Person 

make a function to add personal_data dictionary to persons object. Use this method before giving the list of person objects to the serializer.

def prepare_persons(persons):     person_ids = [p.uuid for p in persons]     personal_data_list = Microservice.objects.custom_filter(uuid__in=person_ids)     personal_data_dict = {pd['uuid']: pd for pd in personal_data_list}     for p in persons:         p.personal_data = personal_data_dict[p.id]     return persons   def list(self, request, *args, **kwargs):      queryset = self.filter_queryset(self.get_queryset())      page = self.paginate_queryset(queryset)      if page is not None:         page = prepare_persons(page)         serializer = self.get_serializer(page, many=True)         return self.get_paginated_response(serializer.data)     else:         persons = prepare_persons(queryset)      serializer = self.get_serializer(persons, many=True)     return Response(serializer.data) 
Read More

Tuesday, February 13, 2018

Django ManyToMany Validation Constraint

Leave a Comment

I have a ManyToMany link, and a Foreign key which links three objects.

[A]>--<[B]>---[C]

A can belong to many of B, and vice versa. However, A can only belong to B objects with the same parent C.

I'm trying to do something in the clean() method of the model. I'm using Django Rest Framework and no ModelForms or anything like that. I haven't been able to figure it out yet

Simplified Sample Code

class Device(models.Model):     name = models.CharField(max_length=20)     projects = models.ManyToManyField(Project, 'devices')     details = models.CharField(max_length=200)     serial = models.CharField(max_length=20)     address models.GenericIPAddressField(default="0.0.0.0")     port = models.IntegerField(default=3000)     jumpers = models.IntegerField(default=0)     install_date = models.DateField(blank=True, null=True)  class Project(models.Model):     name = models.CharField(max_length=20)     description = models.CharField(max_length=250)     area = models.ForeignKey(Area)  class Area(models.Model):     name = models.CharField(max_length=20)     description = models.CharField(max_length=250)     owner = models.CharField(max_length=20)  # microservice doesn't have owner group - field in JWT 

Serializers

class AreaSerializer(serializers.ModelSerializer):      class Meta:         model = Area         fields = ('name', 'description', 'owner')   class ProjectSerializer(serializers.ModelSerializer):      class Meta:         model = Project         fields = ('id', 'name', 'description', 'area')   class DeviceSerializer(serializers.ModelSerializer):     class Meta:         model = Device         fields = ('id', 'name', 'projects', 'details', 'serial',                   'address', 'port', 'jumpers', 'install_date') 

2 Answers

Answers 1

(ignore the wonky field types, cba) enter image description here

What it boils down to is: you need a table BC that stores relations between B and C. Table A would then select only from those relations through the intermediary m2m table ABC (or ditch ABC, couldn't figure out how to draw m2m with the online tool). I think I mixed up B and C in this picture, swap them around depending on whether B or C holds the ForeignKey.
Please correct if I'm wrong!

Answers 2

I am not sure where and how do you want to validate your data. So I am just posting the method which can validate if a project can be linked to a device or not based on your specific check.

def validate_project(device, project):     projects = device.projects.all()     areas = set(projects.values_list('area', flat=True))     if len(areas) > 1:         raise serializers.ValidationError('projects are not valid')             return areas.pop() == project.area_id 

EDIT:

You have to use a intermediate model for storing the relationship between device and project.

class Membership(models.Model):     device = models.ForeignKey(Device, on_delete=models.CASCADE)     project = models.ForeignKey(Project, on_delete=models.CASCADE)     area = models.ForeignKey(Area, on_delete=models.CASCADE) 

use the above membership model to store the many to many relations.

On your device model use this field to define the many to many relation.

projects = models.ManyToManyField(Project, through='Membership')

checkout the docs

Now when you link a device and project you will have explicitly add the area id as well. Before adding now you can check if the project is valid or not based on the area associated.

Read More

Wednesday, January 31, 2018

Extending custom router to default router across apps in Django Rest Framework

Leave a Comment

I have come across a problem regarding having the API apps seperate, while still being able to use the browsable API for navigation.

I have previously used a seperate routers.py file in my main application containing the following extension of the DefaultRouter.

class DefaultRouter(routers.DefaultRouter):     def extend(self, router):         self.registry.extend(router.registry) 

Followed by adding the other application routers like this:

from . routers import DefaultRouter from app1.urls import router as app1_router  # Default Router mainAppRouter = DefaultRouter() mainAppRouter.extend(app1_router) 

where the app1_router is a new SimpleRouter object.

Now the problem occurs when I want to modify the SimpleRouter and create my own App1Router, such as this

class App1Router(SimpleRouter):      routes = [         Route(             url = r'^{prefix}{trailing_slash}$',             mapping = {                 'get': 'retrieve',                 'post': 'create',                 'patch': 'partial_update',             },             name = '{basename}-user',             initkwargs = {}         ),     ] 

This will not handle my extension correctly. As an example, GET and PATCH are not recognized as allowed methods whenever I extend the router, but when I dont extend, but only use the custom router, everything works fine.

My question is therefor, how can I handle extending custom routers across seperate applications, but still maintain a good browsable API?

0 Answers

Read More

Tuesday, August 15, 2017

How to access serializer.data on ListSerializer parent class in DRF?

Leave a Comment

I'm getting an error when trying to access serializer.data before returning it in the Response(serializer.data, status=something):

Getting KeyError when attempting to get a value for field <field> on serializer <serializer>.

This occurs on all fields (because it turns out I'm trying to access .data on the parent and not the child, see below)

The class definition looks like this:

class BulkProductSerializer(serializers.ModelSerializer):      list_serializer_class = CustomProductListSerializer      user = serializers.CharField(source='fk_user.username', read_only=False)      class Meta:         model = Product         fields = (             'user',             'uuid',             'product_code',             ...,         ) 

CustomProductListSerializer is a serializers.ListSerializer and has an overridden save() method that allows it to correctly handle bulk create and update.

Here's an example view from the bulk Product ViewSet:

def partial_update(self, request):      serializer = self.get_serializer(data=request.data,                         many=isinstance(request.data, list),                         partial=True)     if not serializer.is_valid():         return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)     serializer.save()     pdb.set_trace()     return Response(serializer.data, status=status.HTTP_200_OK) 

Trying to access serializer.data at the trace (or the line after, obviously) causes the error. Here's the full trace (tl;dr skip below where I diagnose with debugger):

 Traceback (most recent call last):   File "/lib/python3.5/site-packages/django/core/handlers/exception.py", line 41, in inner     response = get_response(request)   File "/lib/python3.5/site-packages/django/core/handlers/base.py", line 249, in _legacy_get_response     response = self._get_response(request)   File "/lib/python3.5/site-packages/django/core/handlers/base.py", line 187, in _get_response     response = self.process_exception_by_middleware(e, request)   File "/lib/python3.5/site-packages/django/core/handlers/base.py", line 185, in _get_response     response = wrapped_callback(request, *callback_args, **callback_kwargs)   File "/lib/python3.5/site-packages/django/views/decorators/csrf.py", line 58, in wrapped_view     return view_func(*args, **kwargs)   File "/lib/python3.5/site-packages/rest_framework/viewsets.py", line 86, in view     return self.dispatch(request, *args, **kwargs)   File "/lib/python3.5/site-packages/rest_framework/views.py", line 489, in dispatch     response = self.handle_exception(exc)   File "/lib/python3.5/site-packages/rest_framework/views.py", line 449, in handle_exception     self.raise_uncaught_exception(exc)   File "/lib/python3.5/site-packages/rest_framework/views.py", line 486, in dispatch     response = handler(request, *args, **kwargs)   File "/application/siop/views/API/product.py", line 184, in partial_update     return Response(serializer.data, status=status.HTTP_200_OK)   File "/lib/python3.5/site-packages/rest_framework/serializers.py", line 739, in data     ret = super(ListSerializer, self).data   File "/lib/python3.5/site-packages/rest_framework/serializers.py", line 265, in data     self._data = self.to_representation(self.validated_data)   File "/lib/python3.5/site-packages/rest_framework/serializers.py", line 657, in to_representation     self.child.to_representation(item) for item in iterable   File "/lib/python3.5/site-packages/rest_framework/serializers.py", line 657, in <listcomp>     self.child.to_representation(item) for item in iterable   File "/lib/python3.5/site-packages/rest_framework/serializers.py", line 488, in to_representation     attribute = field.get_attribute(instance)   File "/lib/python3.5/site-packages/rest_framework/fields.py", line 464, in get_attribute     raise type(exc)(msg) KeyError: "Got KeyError when attempting to get a value for field `user` on serializer `BulkProductSerializer`.\nThe serializer field might be named incorrectly and not match any attribute or key on the `OrderedDict` instance.\nOriginal exception text was: 'fk_user'." 

At the L657 of the traceback (source here) I've got:

iterable = data.all() if isinstance(data, models.Manager) else data return [     self.child.to_representation(item) for item in iterable ] 

This made me wonder (digging further down in the trace) why the serializer.fields were not available. I suspected it was because the serializer was a CustomProductListSerializer parent, and not a BulkProductSerializer child, and I was right. In the pdb trace just before returning the Response(serializer.data):

(Pdb) serializer.fields *** AttributeError: 'CustomProductListSerializer' object has no attribute 'fields' (Pdb) serializer.child.fields {'uuid': UUIDField(read_only=False, required=False, validators=[]) ...(etc)} (Pdb) 'user' in serializer.child.fields True (Pdb) serializer.data *** KeyError: "Got KeyError when attempting to get a value for field `user` on serializer `BulkProductSerializer`.\nThe serializer field might be named incorrectly and not match any attribute or key on the `OrderedDict` instance.\nOriginal exception text was: 'fk_user'." (Pdb) serializer.child.data {'uuid': '08ec13c0-ab6c-45d4-89ab-400019874c63', ...(etc)} 

OK, so what's the right way to get the complete serializer.data and return it in the resopnse for the parent serializer class in the situation described by partial_update in my ViewSet?

Edit:

class CustomProductListSerializer(serializers.ListSerializer):      def save(self):         instances = []         result = []         pdb.set_trace()         for obj in self.validated_data:             uuid = obj.get('uuid', None)             if uuid:                 instance = get_object_or_404(Product, uuid=uuid)                 # Specify which fields to update, otherwise save() tries to SQL SET all fields.                 # Gotcha: remove the primary key, because update_fields will throw exception.                 # see https://stackoverflow.com/a/45494046                 update_fields = [k for k,v in obj.items() if k != 'uuid']                 for k, v in obj.items():                     if k != 'uuid':                         setattr(instance, k, v)                 instance.save(update_fields=update_fields)                 result.append(instance)             else:                 instances.append(Product(**obj))          if len(instances) > 0:             Product.objects.bulk_create(instances)             result += instances          return result 

4 Answers

Answers 1

As mentioned in the comment i still think the exception could be because of the user field in BulkProductSerializer class, not really anything to do with ListSerializer

There might be another minor error (but important) in the serializer DRF as mentioned in the documentation here. Here is how to specify a list_serializer_class:

class CustomListSerializer(serializers.ListSerializer):     ...  class CustomSerializer(serializers.Serializer):     ...     class Meta:         list_serializer_class = CustomListSerializer 

Note that it's specified inside of the Meta class, not outside. So i think in your code, it will not understand to switch to the List Serializer with many=True. That should cause the not-updating problem.

Answers 2

At the point in the trace where I try to access serializer.data and get the KeyError, I note that serializer.data only contains key/vaule pairs from the initial_data, not the instance data (hence, I suppose, the KeyError; some model fields' keys are not present as it is a partial_update request). However, serializer.child.data does contain all the instance data for the last child in the list.

So, I go to the rest_framework/serializers.py source where data is defined:

249    @property 250    def data(self): 251        if hasattr(self, 'initial_data') and not hasattr(self, '_validated_data'): 252            msg = ( 253                'When a serializer is passed a `data` keyword argument you ' 254                'must call `.is_valid()` before attempting to access the ' 255                'serialized `.data` representation.\n' 256                'You should either call `.is_valid()` first, ' 257                'or access `.initial_data` instead.' 258            ) 259            raise AssertionError(msg) 260 261        if not hasattr(self, '_data'): 262            if self.instance is not None and not getattr(self, '_errors', None): 263                self._data = self.to_representation(self.instance) 264            elif hasattr(self, '_validated_data') and not getattr(self, '_errors', None): 265                self._data = self.to_representation(self.validated_data) 266            else: 267                self._data = self.get_initial() 268        return self._data 

Line 265 is problematic. I can replicate the error by calling serializer.child.to_representation({'uuid': '87956604-fbcb-4244-bda3-9e39075d510a', 'product_code': 'foobar'}) at the breakpoint.

Calling partial_update() works fine on a single instance (because self.instance is set, self.to_representation(self.instance) works). However, for a bulk partial_update() implementation, self.validated_data is missing model fields, and to_representation() won't work, so I won't be able to access the .data property.

One option would be to maintain some sort of self.instances list of Product instances, and override the definition of data on line 265:

self._data = self.to_representation(self.instances) 

I'd really prefer an answer from someone more experienced in this sort of problem though, as I'm not sure if that's a sensible solution, hence I'm leaving the bounty open in the hope that someone can suggest something smarter to do.

Answers 3

Remove source if you are using Django auth model and set read_only=True.

user = serializers.CharField(read_only=True)

Hope this works for you

Answers 4

You have defined user field on BulkProductSerializer as writable but have not told the serializer how to handle it...

The easiest way to correct this is to use a SlugRelatedField:

class BulkProductSerializer(serializers.ModelSerializer):      list_serializer_class = CustomProductListSerializer      user = serializers.SlugRelatedField(                             slug_field='username',                             queryset=UserModel.objects.all(),                             source='fk_user'     )      class Meta:         model = Product         fields = (             'user',             'uuid',             'product_code',             ...,         ) 

This should handle nicely errors, for example when username does not exist...

Read More

Wednesday, August 2, 2017

Write a wrapper to expose existing REST APIs as SOAP web services?

Leave a Comment

I have existing REST APIs, written using Django Rest Framework and now due to some client requirements I have to expose some of them as SOAP web services.

I want to know how to go about writing a wrapper in python so that I can expose some of my REST APIs as SOAP web services. OR should I make SOAP web services separately and reuse code ?

I know this is an odd situation but any help would be greatly appreciated.

3 Answers

Answers 1

You can say, SOAP and REST are basically apples and oranges.

You basically need something, where you can consume the REST API's.

As I see, you have some options:

  • Use a SOAP service separately running on another port (endpoint). For that I would say, use framework's like Spyne check out sample hello world
  • Use the clients preferred way, either SOAP via WSGI or SOAP via HttpRPC
  • Invoke the same REST API endpoints which you created via the methods in SOAP. We had used an internal api wrapper in one of application, which is as:
def wrap_internal_api_call(requests_api_method, uri,                             data, cookies=None, headers=None):      return requests_api_method(uri, data=data, files=files,                 cookies=cookies, headers=headers)

How you can use this?

import requests  from django.core.urlresolvers import reverse from django.conf import settings  from spyne.service import Service from spyne.decorator import srpc from spyne.model import ByteArray, DateTime, Uuid, String, Integer, Integer8, \     ComplexModel, Array   # This method will hit the internal API which is written in DJANGO REST FRAMEWORK def build_internal_uri(uri):   return 'http://localhost:{0}{1}'.format(settings.INTERNAL_API_PORT, uri)   class RequestHeader(ComplexModel):   some_field = String   class SomeService(Service):     # Headers related doc     # https://github.com/arskom/spyne/blob/68b9d5feb71b169f07180aaecfbe843d8ba500bf/doc/source/manual/06_metadata.rst#protocol-headers      __in_header__ = RequestHeader    @srpc(String, _returns=String)   def echo_string(s):     headers = ctx.in_header.some_field      # Reverse url from the urls.py file     local_order_fetch_url = build_internal_uri(reverse('website:order_details')) + '?order_id=' + order_id      response = wrap_internal_api_call(requests.get, local_order_fetch_url,              { 'data': 'sample_data' }, None, headers)      return response['data'] # Some string data   app = Application([SomeService], 'tns', in_protocol=HttpRpc(parse_cookie=True),                  out_protocol=HttpRpc()) 

Now there are some of examples which you can look into, being the Django configuration for making it available

Answers 2

Yes, you better follow @Nagaraj Tantri he has given you the best answer possible.

1) Use a SOAP service separately running on another port (endpoint). For that I would say, use framework's like Spyne check out sample hello world 2) Use the clients preferred way, either SOAP via WSGI or SOAP via HttpRPC 3) Invoke the same REST API endpoints which you created via the methods in SOAP. We had used an internal api wrapper in one of application.

Answers 3

Lets Discuss both the Approaches and their pros and cons

SOAP

  1. Reusing Same Code - if you are sure the code changes will not impact the two code flow ,it is good to go.
  2. Extension of Features - if you are sure that new feature extension will not impact other parts it is again best to go.
  3. Scalablity - if new API are part of same application and you are sure that it will be scalable with more load ,it is again a good option.
  4. Extension - if you are sure in future adding more API will not create a mess of code, it is again good to go for.

REST (my favourate and suggested way to go)

Answer for all the above question in case of rest is YES.

Few more additions benifits

Multiple device support ,universally accepted.

Your Call ,

Comments and critisicsm are most welcome

Read More

Sunday, June 11, 2017

Testing Django Rest Framework POST returns 500 despite call working

Leave a Comment

Update

This issue was caused by me not including a token in the APIClient's header. This is resolved.


I have a standard ModelViewSet at /test-endpoint. I am trying to use APIClient to test the endpoint.

from rest_framework.test import APIClient ... # During this process, a file is uploaded to S3. Could this cause the issue? Again, no errors are thrown. I just get a 500. self.client = APIClient() ... sample_call = {     "name": "test_document",     "description": "test_document_description" } response = self.client.post('/test-endpoint', sample_call, format='json') self.assertEqual(response.status_code, 201) 

This call works with the parameters I set in sample_call. It returns a 201. When I run the test, however, I get a 500. How can I modify this to get the 201 passed?

I run the tests with python src/manage.py test modulename


To rule out the obvious, I copy-pasted the sample call into Postman and run it without issue. I believe the 500 status code is coming from the fact that I'm testing the call and not using it in a live environment.


No error messages are being thrown beyond the AssertionError:

AssertionError: 500 != 201

Full Output of testing

/home/bryant/.virtualenvs/REDACTED/lib/python3.4/site- packages/django_boto/s3/shortcuts.py:28: RemovedInDjango110Warning:  Backwards compatibility for storage backends without support for the     `max_length` argument in Storage.get_available_name() will be removed in Django 1.10. s3.save(full_path, fl)  F ====================================================================== FAIL: test_create (sample.tests.SampleTestCase) Test CREATE Document ---------------------------------------------------------------------- Traceback (most recent call last): File "/home/bryant/api/redacted/src/sample/tests.py", line 31, in test_create self.assertEqual(response.status_code, 201) AssertionError: 500 != 201  ---------------------------------------------------------------------- Ran 1 test in 2.673s  FAILED (failures=1) Destroying test database for alias 'default'... 

The S3 warning is expected. Otherwise, all appears normal.

1 Answers

Answers 1

To debug a failing test case in Django/DRF:

  • Put import pdb; pdb.set_trace() just before the assertion and see the request.content as suggested by @Igonato, or you can just add a print(request.content), there is no shame for it.

  • Increase verbosity of your tests by adding -v 3

  • Use dot notation to investigate the specific test case: python src/manage.py test modulename.tests.<TestCase>.<function>

I hope these are useful to keep in mind.

Read More

Thursday, June 8, 2017

Upload image file using django rest framework in a single page application

Leave a Comment

I am trying to upload image using Vuejs and Django, but I can't figure out how to solve it.

This is the django side:

class UserDetail(models.Model):     user = models.OneToOneField(User)     profile_picture = models.ImageField(upload_to=create_file_path)  class UserDetailSerializer(serializers.ModelSerializer):     class Meta:         model = UserDetail         fields = '__all__'  class UserDetailViewSet(viewsets.ModelViewSet):     queryset = UserDetail.objects.all()     serializer_class = UserDetailSerializer     permission_classes = [AllowAny]      @detail_route(permission_classes=[AllowAny], methods=['POST'], parser_classes=[FormParser, MultiPartParser])     def create_or_update_profile_picture(self, request):         user = request.user         #         # how to create or update user detail profile picture ?         # 

I am posting the data this way from Vuejs:

changeProfilePicture() {     const file_input = document.getElementById('display_profile_image');     const img = file_input.files[0];     let formData = new FormData();     formData.append("profile_picture", img);     const url = this.$store.state.website + '/api/accounts/user-detail/none/create_or_update_profile_picture/';     this.$http.post(url, formData)         .then(function (response) {             this.$store.dispatch('getUserDetail');         })         .catch(function (response) {             console.log(response);         }); } 

How can I use the post data to create or update the request.user's profile_picture with Django and django rest framework inside the model viewset class, using default methods (create/update/partial_update) or by creating a new detail route?

2 Answers

Answers 1

here is example in official documentation:

http://www.django-rest-framework.org/api-guide/parsers/#fileuploadparser

Answers 2

Assuming your JS posts the request using 'multipart/form-data' (check this), you should be able to upload the image file when creating or updating the user. Also, make sure you send CSRF Token.

To be able to set the logo on its own, a detailed_route is a good way using a serializer limited to the logo. In the detailed route, if you want to upload log for logged in user (I saw you put none as an id), you can check that in the detail route before calling get_object which will get the userdetail instance.

class UserLogoSerializer(serializers.ModelSerializer):     class Meta:         model = UserDetail         fields = ['profile_picture']   class UserDetailViewSet(viewsets.ModelViewSet):     queryset = UserDetail.objects.all()     serializer_class = UserDetailSerializer     permission_classes = [AllowAny]      @detail_route(methods=['post'])     def set_profile_picture(self, request, pk=None, format=None):         if pk in ['none', 'self']: # shortcut to update logged in user without looking for the id             try:                 userdetail = self.get_queryset().get(user=request.user)             except UserDetail.DoesNotExist:                 userdetail = None         else:             userdetail = self.get_object()          serializer = serializers.UserLogoSerializer(userdetail, data=request.data)         if serializer.is_valid():             serializer.save()             return Response(serializer.data)                      return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) 

If I recall correctly, permission_classes are the one set on the Viewset by default, and the default parsers should do the job.

Read More

Thursday, May 4, 2017

Django rest framework group by fields and add extra contents

Leave a Comment

I have a Ticket booking model

class Movie(models.Model):     name = models.CharField(max_length=254, unique=True)  class Show(models.Model):     day = models.ForeignKey(Day)     time = models.TimeField(choices=CHOICE_TIME)     movie = models.ForeignKey(Movie)  class MovieTicket(models.Model):     show = models.ForeignKey(Show)     user = models.ForeignKey(User)     booked_at = models.DateTimeField(default=timezone.now) 

I would like to filter MovieTicket with its user field and group them according to its show field, and order them by the recent booked time. And respond back with json data using Django rest framework like this:

[     {         show: 4,         movie: "Lion king",         time: "07:00 pm",         day: "23 Apr 2017",         total_tickets = 2     },     {         show: 7,         movie: "Gone girl",         time: "02:30 pm",         day: "23 Apr 2017",         total_tickets = 1     } ] 

I tried this way:

>>> MovieTicket.objects.filter(user=23).order_by('-booked_at').values('show').annotate(total_tickets=Count('show')) <QuerySet [{'total_tickets': 1, 'show': 4}, {'total_tickets': 1, 'show': 4}, {'total_tickets': 1, 'show': 7}]> 

But its not grouping according to the show. Also how can I add other related fields (i.e., show__movie__name, show__day__date, show__time)

2 Answers

Answers 1

You have to group by show and then count the total number of movie tickets.

MovieTicket.objects.filter(user=23).values('show').annotate(total_tickets=Count('show')).values('show', 'total_tickets', 'show__movie__name', 'show__time', 'show__day__date')) 

Use this serilizer class for the above queryset. It will give the required json output.

class MySerializer(serializers.Serializer):     show = serailizer.IntegerField()     movie = serializer.StringField(source='show__movie__name')     time = serializer.TimeField(source='show__time')     day = serializer.DateField(source='show__day__date')     total_tickets = serializer.IntegerField() 

It is not possible to order_by booked_at since that information gets lost when we group by show. If we order by booked_at group by will happen on unique booked_at times and show ids and that is why the ticket count was coming 1. Without order_by you will get correct count.

EDIT:

use this query:

queryset = (MovieTicket.objects.filter(user=23)             .order_by('booked_at').values('show')             .annotate(total_tickets=Count('show'))             .values('show', 'total_tickets', 'show__movie__name',                     'show__time', 'show__day__date'))) 

You cannot annotate on an annotated field. So you will to find the total tickets count in python. To calculate total_tickets count for unique show ids:

tickets = {} for obj in queryset:     if obj['show'] not in tickets.keys():         tickets[obj['show']] = obj     else:         tickets[obj['show']]['total_tickets'] += obj['total_tickets'] 

the final list of objects you need is tickets.values()

The same serializer above can be used with these objects.

Answers 2

I would like to filter MovieTicket with its user field and group them according to its show field, and order them by the recent booked time.

This queryset will give you exactly what you want:

tickets = (MovieTicket.objects             .filter(user=request.user)             .values('show')             .annotate(last_booking=Max('booked_at'))             .order_by('-last_booking') ) 

And respond back with json data using Django rest framework like this: [ { show: 4, movie: "Lion king", time: "07:00 pm", day: "23 Apr 2017", total_tickets = 2 }, { show: 7, movie: "Gone girl", time: "02:30 pm", day: "23 Apr 2017", total_tickets = 1 } ]

Well this json data is not the same as the query you described. You can add total_tickets by extending the annotation and show__movie__name into the .values clause: this will change the grouping to show+movie_name, but since show only has one movie_name it wont matter.

However, you cannot add show__day__date and show__time, because one show have multiple date-times, so which one would you want from a group? You could for example fetch the maximum day and time but this does not guarantee you that at this day+time there will be a show, because these are different fields, not related by each other. So the final attempt may look like:

tickets = (MovieTicket.objects             .filter(user=request.user)             .values('show', 'show__movie__name')             .annotate(                 last_booking=Max('booked_at'),                 total_tickets=Count('pk'),                 last_day=Max('show__day'),                 last_time=Max('show__time'),             )             .order_by('-last_booking') ) 
Read More