Register a SA Forums Account here!
JOINING THE SA FORUMS WILL REMOVE THIS BIG AD, THE ANNOYING UNDERLINED ADS, AND STUPID INTERSTITIAL ADS!!!

You can: log in, read the tech support FAQ, or request your lost password. This dumb message (and those ads) will appear on every screen until you register! Get rid of this crap by registering your own SA Forums Account and joining roughly 150,000 Goons, for the one-time price of $9.95! We charge money because it costs us money per month for bills, and since we don't believe in showing ads to our users, we try to make the money back through forum registrations.
 
  • Post
  • Reply
epswing
Nov 4, 2003

Soiled Meat
I have a DRF one-to-many relationship question.

Say I have the following simple one-to-many setup with Customers and Contacts:

Python code:
class Customer(models.Model):
    name = models.CharField(max_length=100)

class Contact(models.Model):    
    customer = models.ForeignKey(Customer, on_delete=models.CASCADE, related_name='contacts')
    name = models.CharField(max_length=100)
I think Contacts only make sense in the context of a Customer, which is why to add/remove/update contacts, you need to specify which customer you're dealing with:

code:
/customers/                    # GET, POST        aka class CustomerList(generics.ListCreateAPIView)
/customers/:int/               # GET, PUT, DELETE aka class CustomerDetail(generics.RetrieveUpdateDestroyAPIView)
/customers/:int/contacts/      # GET, POST        aka class ContactList(generics.ListCreateAPIView)
/customers/:int/contacts/:int/ # GET, PUT, DELETE aka class ContactDetail(generics.RetrieveUpdateDestroyAPIView)
I'm a little stumped as to how I should represent this with serializers/views. I'm looking at http://www.django-rest-framework.org/api-guide/relations/#custom-hyperlinked-fields but I'm not sure if I'm barking up the wrong tree...

Adbot
ADBOT LOVES YOU

Thermopyle
Jul 1, 2003

...the stupid are cocksure while the intelligent are full of doubt. —Bertrand Russell

epalm posted:

I have a DRF one-to-many relationship question.

Say I have the following simple one-to-many setup with Customers and Contacts:

Python code:
class Customer(models.Model):
    name = models.CharField(max_length=100)

class Contact(models.Model):    
    customer = models.ForeignKey(Customer, on_delete=models.CASCADE, related_name='contacts')
    name = models.CharField(max_length=100)
I think Contacts only make sense in the context of a Customer, which is why to add/remove/update contacts, you need to specify which customer you're dealing with:

code:
/customers/                    # GET, POST        aka class CustomerList(generics.ListCreateAPIView)
/customers/:int/               # GET, PUT, DELETE aka class CustomerDetail(generics.RetrieveUpdateDestroyAPIView)
/customers/:int/contacts/      # GET, POST        aka class ContactList(generics.ListCreateAPIView)
/customers/:int/contacts/:int/ # GET, PUT, DELETE aka class ContactDetail(generics.RetrieveUpdateDestroyAPIView)
I'm a little stumped as to how I should represent this with serializers/views. I'm looking at http://www.django-rest-framework.org/api-guide/relations/#custom-hyperlinked-fields but I'm not sure if I'm barking up the wrong tree...

I'd probably denormalize from two separate models to just one Customer model that contains the fields from Contact.

epswing
Nov 4, 2003

Soiled Meat

Thermopyle posted:

I'd probably denormalize from two separate models to just one Customer model that contains the fields from Contact.

For simple cases, maybe, but when I have Customers, that have Jobs, that have Orders, that have OrderLines, that have a Product, I really don't think I should have a single endpoint and a massively nested serializer.

My example is intentionally simple, but I'm looking ahead to solving larger and more complex problems.

Thermopyle
Jul 1, 2003

...the stupid are cocksure while the intelligent are full of doubt. —Bertrand Russell

epalm posted:

For simple cases, maybe, but when I have Customers, that have Jobs, that have Orders, that have OrderLines, that have a Product, I really don't think I should have a single endpoint and a massively nested serializer.

My example is intentionally simple, but I'm looking ahead to solving larger and more complex problems.

Ok, if you're simplifying your example and you actually need a separate Contact model...

I strive to avoid nested serialization.

In your simple example, I'd require two requests. One for creating/getting each model type. This makes your code simple on both the frontend and the backend at the cost of extra requests. Don't worry about the extra requests unless benchmarking reveals it to be a problem.

So, on your frontend POST a new customer, then POST a new contact with the id of the customer as one of the fields.

Getting a list of contacts for a customer, you just have a list view that gets the id for the customer from the url.

Ahz
Jun 17, 2001
PUT MY CART BACK? I'M BETTER THAN THAT AND YOU! WHERE IS MY BUTLER?!
I find it's easier to just go all nested with serializers when performance and scaling doesn't matter. It's dirt simple to nest them and only go the depth you need for the fields you need and you end up with a nicely normalized JSON response.

When performance does matter, when I might get into a better custom filter with better prefetching and go with a much more denormalized custom basic serializer just for that view.

When performance matters even more, I'll drop right into plain SQL and serialize things myself as raw data from the recordset.

No point optimizing when its unnecessary. I find DRF is great for convenience features, but poor at optimizing things, so I pretty much stay within DRF for everything until it matters then I drop right out because trying to optimize within viewsets or whatever twisting DRF models around, it's more work than its worth.

epswing
Nov 4, 2003

Soiled Meat

Ahz posted:

I find it's easier to just go all nested with serializers when performance and scaling doesn't matter. It's dirt simple to nest them and only go the depth you need for the fields you need and you end up with a nicely normalized JSON response.

What about writable nested serializers? For example, I'm looking to PUT a parent, and have the children be updated/added/removed by overriding a serializer's update() function, but I can't quite get it to work so far. Basically the equivalent of, if I wasn't using DRF, POST'ing a form + formset. What should a good update() function look like to do this?

Some related examples:
https://django.cowhite.com/blog/create-and-update-django-rest-framework-nested-serializers/
https://stackoverflow.com/a/39138560/466011
https://www.reddit.com/r/django/comments/4jgov5/djangorestframework_updating_nested_serializer/d38nnm4/

epswing fucked around with this message at 00:45 on Dec 3, 2017

Ahz
Jun 17, 2001
PUT MY CART BACK? I'M BETTER THAN THAT AND YOU! WHERE IS MY BUTLER?!

epalm posted:

What about writable nested serializers? For example, I'm looking to PUT a parent, and have the children be updated/added/removed by overriding a serializer's update() function, but I can't quite get it to work so far. Basically the equivalent of, if I wasn't using DRF, POST'ing a form + formset. What should a good update() function look like to do this?

gently caress writing with serializers or saving.

I find saving is always so custom with one POST updating 3-6 tables, I don't bother.

I use plain serializers to validate input as it comes in, sometimes custom transformation post validation, then back to my models for CRUD. If I'm feeling special, I might use a model validator to inherit field validation routines, but never nest or use them for anything fancy or with foreign key data.

epswing
Nov 4, 2003

Soiled Meat

Ahz posted:

gently caress writing with serializers or saving.

I find saving is always so custom with one POST updating 3-6 tables, I don't bother.

I use plain serializers to validate input as it comes in, sometimes custom transformation post validation, then back to my models for CRUD. If I'm feeling special, I might use a model validator to inherit field validation routines, but never nest or use them for anything fancy or with foreign key data.

POST doesn't seem too bad, but yeah, I'm getting the sense that PUT is a little crazy.

Python code:
class ContactSerializer(serializers.ModelSerializer):
    class Meta:
        model = Contact
        fields = ('id', 'name', 'email')

class CustomerSerializer(serializers.ModelSerializer):
    contacts = ContactSerializer(many=True, read_only=False)
    class Meta:
        model = Customer
        fields = ('id', 'name', 'contacts')

    def create(self, validated_data):
        contacts = validated_data.pop('contacts', None)
        customer = Customer(**validated_data)
        customer.save()
        for contact in contacts:
            Contact.objects.create(customer=customer, **contact)
        return customer

    def update(self, instance, validated_data):
        print(validated_data)
        return instance
In the code above, that create() function looks reasonable. But I don't see how I can complete the update() function, because if I PUT the following

pre:
{
  "id": 1,
  "name": "cust",
  "contacts": [
    {
      "id": 1,
      "nam": "cont1",
      "email": "cont1@example.com"
    },
    {
      "id": 2,
      "name": "cont2",
      "email": "cont2@example.com"
    }
  ]
}
It prints this:

pre:
{'name': 'cust', 'contacts': [OrderedDict([('name', 'cont1'), ('email', 'cont1@example.com')]), OrderedDict([('name', 'cont2'), ('email', 'cont2@example.com')])]}
I'm not sure why but the 'id' properties are missing, so how can I hope to update those contacts, let alone add/remove the new/deleted ones.

Thermopyle
Jul 1, 2003

...the stupid are cocksure while the intelligent are full of doubt. —Bertrand Russell

There is no general update function for nested writes, otherwise it would just be part of DRF.

You have to write custom logic, and it's kind of hairy past your most simple cases.

Ahz
Jun 17, 2001
PUT MY CART BACK? I'M BETTER THAN THAT AND YOU! WHERE IS MY BUTLER?!
There's always more custom in the real world. All of a sudden you need your request obj in there for user logging, then whoops you need to validate against another table for permissions blah blah.

porksmash
Sep 30, 2008
Django 2.0 has been released featuring new URL routing syntax and support for database window functions.

Ghost of Reagan Past
Oct 7, 2003

rock and roll fun

porksmash posted:

Django 2.0 has been released featuring new URL routing syntax and support for database window functions.
Also, it drops support for Python 2.

Which, good.

IAmKale
Jun 7, 2007

やらないか

Fun Shoe
I'm cross-posting from the Python thread:

I have a bit of a conundrum: how do I lint my code when my application's Python environment is contained in a Docker container?

I just spun up a new Django project and stumbled a bit when I realized that I don't know what environment path to point the linter to. This is preventing Flake8 from understanding that I have Django installed, and it's outputting typical linting issues like the E0401 one below:



I feel as though my only option is to maintain a virtual environment on my machine that I'll have to keep in sync with the dependencies running in the Docker image that's serving the actual application. Unless there's a better way?

Ahz
Jun 17, 2001
PUT MY CART BACK? I'M BETTER THAN THAT AND YOU! WHERE IS MY BUTLER?!

IAmKale posted:

I'm cross-posting from the Python thread:

I have a bit of a conundrum: how do I lint my code when my application's Python environment is contained in a Docker container?

I just spun up a new Django project and stumbled a bit when I realized that I don't know what environment path to point the linter to. This is preventing Flake8 from understanding that I have Django installed, and it's outputting typical linting issues like the E0401 one below:



I feel as though my only option is to maintain a virtual environment on my machine that I'll have to keep in sync with the dependencies running in the Docker image that's serving the actual application. Unless there's a better way?

Sounds like a pain in the rear end to remote dev on a docker container when you can just manage dependencies via virtualenv for most of your needs. One way to do it would be to setup ssh direct to the container's local path and run your interpreter via ssh to that.

epswing
Nov 4, 2003

Soiled Meat
I don't really understand DRF's ReadOnlyField.

quote:

This field is used by default with ModelSerializer when including field names that relate to an attribute rather than a model field.

Signature: ReadOnlyField()

For example, if has_expired was a property on the Account model, then the following serializer would automatically generate it as a ReadOnlyField:

code:
class AccountSerializer(serializers.ModelSerializer):
    class Meta:
        model = Account
        fields = ('id', 'account_name', 'has_expired')

What does a "field name that relates to an attribute rather than a model field" mean?

When they say "has_expired was a property on the Account model", what is a property vs attribute vs field?

I'm trying to understand how to send the client of my api some data that is understood to be readonly, for example a created_timestamp field that is a DateTimeField. I want to include created_timestamp on the GET, but ignore it on the subsequent PUT. It sounds like ReadOnlyField might be what I'm looking for, but the description is confusing to me.

Thermopyle
Jul 1, 2003

...the stupid are cocksure while the intelligent are full of doubt. —Bertrand Russell

epalm posted:

I don't really understand DRF's ReadOnlyField.


What does a "field name that relates to an attribute rather than a model field" mean?

When they say "has_expired was a property on the Account model", what is a property vs attribute vs field?

I'm trying to understand how to send the client of my api some data that is understood to be readonly, for example a created_timestamp field that is a DateTimeField. I want to include created_timestamp on the GET, but ignore it on the subsequent PUT. It sounds like ReadOnlyField might be what I'm looking for, but the description is confusing to me.

A Django model can have attributes that aren't Django model fields because Django models are just Python classes.

For example:

Python code:
class Person(models.Model):
    first_name = models.CharField
    last_name = models.CharField

    @property
    def full_name(self):
        return f"{self.first_name} {self.last_name}"

>>> print(person_instance.full_name)
Sarah Smith
You would use a ReadOnlyField for full_name. In this case, it doesn't make any sense to write to full_name.

In your case I'd do something like:

Python code:
class Whatever(serializers.ModelSerializer):
    created_timestamp = serializers.DateTimeField(read_only=True)

Data Graham
Dec 28, 2009

📈📊🍪😋



Yeah. Your created_timestamp is an actual model field (column in the database), so the documentation you quote doesn't apply to it; it won't create a ReadOnlyField automatically. You'll want to use the read_only=True flag on a DateTimeField like Thermopyle says, to make sure it gets serialized properly.

Defined @properties (what that doc calls attributes) are like the biggest "aha" thing about Django, to take a tangent. Rather than making direct SQL queries and having all your derived logic take place in the view or the template like in old-school frameworks, Django wants you to move all that logic as far back toward the model as you can, so ideally any logic that depends only on the model itself (i.e. where the output can be generated using only the properties of "self", as opposed to needing extra context like the user or the request) should go into model code. That way you can use the model instance in downstream code, like in a view, without having to think about whether it's a raw database field that you're interacting with or just another object property—ideally they all act like object properties and you can treat all your interactions like python code instead of SQL.

epswing
Nov 4, 2003

Soiled Meat

Thermopyle posted:

A Django model can have attributes that aren't Django model fields because Django models are just Python classes.

Ah ok it's for non-database properties. Cool.

Data Graham posted:

Defined @properties (what that doc calls attributes)

This is also what I needed, properties and attributes are the same thing. Thanks!

epswing fucked around with this message at 23:23 on Jan 7, 2018

Fluue
Jan 2, 2008
@properties is the only thing keeping my sanity right now. We use it on a model to reference API-driven values (since the API holds all the actual information, not Django). We added a bunch of @properties to the model that let us access API values like model attributes, e.g.
code:
blah = MyModel.objects.get(some_id='abc123')
print(blah.first_name)
> 'Joe'
print(blah.zip_code)
> 90210
Where the first_name and zip_code values are actually retrieved from an external API response (the response is object-level cached, so we don't make a request for every property)

epswing
Nov 4, 2003

Soiled Meat
There's something about annotate I don't understand.

Using Django 1.11, say I have the following models:

Python code:
class School(models.Model):
    pass
    
class Classroom(models.Model):
    school = models.ForeignKey(School)
    active = models.BooleanField()
    busy = models.BooleanField()
    
class Chalkboard(models.Model):
    classroom = models.ForeignKey(Classroom)
    
class Whiteboard(models.Model):
    classroom = models.ForeignKey(Classroom)
And I create a school, with a classroom, which has 2 whiteboards and 2 chalkboards:
code:
s = School()
s.save()

c = Classroom(school=s, active=True, busy=False)
c.save()

Chalkboard(classroom=c).save()
Chalkboard(classroom=c).save()
Whiteboard(classroom=c).save()
Whiteboard(classroom=c).save()
I want a summary of how many chalkboards and whiteboards there are at each school that is active but not busy.

If I write my query like this, q[0].chalkboard_count is 4 (I expect it to be 2):
Python code:
q = School.objects.filter(
    Q(classroom__active=True) & Q(classroom__busy=False)
).annotate(
    chalkboard_count=Count('classroom__chalkboard'),
    whiteboard_count=Count('classroom__whiteboard'),
)

# q[0].chalkboard_count is 4
If I remove the second annotate parameter, q[0].chalkboard_count is 2:
Python code:
q = School.objects.filter(
    Q(classroom__active=True) & Q(classroom__busy=False)
).annotate(
    chalkboard_count=Count('classroom__chalkboard'),
    # removed the whiteboard_count param
)

# q[0].chalkboard_count is 2
What am I doing wrong here?

epswing fucked around with this message at 22:49 on Jan 9, 2018

huhu
Feb 24, 2006

epalm posted:

There's something about annotate I don't understand.

Say I have the following models:
Python code:
class School(models.Model):
    pass
    
class Classroom(models.Model):
    school = models.ForeignKey(School)
    active = models.BooleanField()
    busy = models.BooleanField()
    
class Chalkboard(models.Model):
    classroom = models.ForeignKey(Classroom)
    
class Whiteboard(models.Model):
    classroom = models.ForeignKey(Classroom)
And I create a school, with a classroom, which has 2 whiteboards and 2 chalkboards:
code:
>>> s = School()
>>> s.save()
>>> 
>>> c = Classroom(school=s, active=True, busy=False)
>>> c.save()
>>> 
>>> Chalkboard(classroom=c).save()
>>> Chalkboard(classroom=c).save()
>>> Whiteboard(classroom=c).save()
>>> Whiteboard(classroom=c).save()
I want a summary of how many chalkboards and whiteboards there are at each school that is active but not busy.

If I write my query like this, q[0].chalkboard_count is 4 (I expect it to be 2):
Python code:
q = School.objects.filter(
    Q(classroom__active=True) & Q(classroom__busy=False)
).annotate(
    chalkboard_count=Count('classroom__chalkboard'),
    whiteboard_count=Count('classroom__whiteboard'),
)

# q[0].chalkboard_count is 4
If I remove the second annotate parameter, q[0].chalkboard_count is 2:
Python code:
q = School.objects.filter(
    Q(classroom__active=True) & Q(classroom__busy=False)
).annotate(
    chalkboard_count=Count('classroom__chalkboard'),
    # removed the whiteboard_count param
)

# q[0].chalkboard_count is 2
What am I doing wrong here?

From here:
https://docs.djangoproject.com/en/2.0/topics/db/aggregation/

It looks like you should be chaining them:
code:
>>> from django.db.models import Q
>>> above_5 = Count('book', filter=Q(book__rating__gt=5))
>>> below_5 = Count('book', filter=Q(book__rating__lte=5))
>>> pubs = Publisher.objects.annotate(below_5=below_5).annotate(above_5=above_5)
>>> pubs[0].above_5
23
>>> pubs[0].below_5
12
Perhaps your issue with 4 is from:

quote:

Each argument to annotate() describes an aggregate that is to be calculated.

Also:

quote:

Combining multiple aggregations with annotate() will yield the wrong results because joins are used instead of subqueries:
code:
>>> book = Book.objects.first()
>>> book.authors.count()
2
>>> book.store_set.count()
3
>>> q = Book.objects.annotate(Count('authors'), Count('store'))
>>> q[0].authors__count
6
>>> q[0].store__count
6

epswing
Nov 4, 2003

Soiled Meat

huhu posted:

From here:
https://docs.djangoproject.com/en/2.0/topics/db/aggregation/

It looks like you should be chaining them

Chaining them doesn't seem to help:

code:
>>> q = School.objects.filter(
...     Q(classroom__active=True) & Q(classroom__busy=False)
... ).annotate(
...     chalkboard_count=Count('classroom__chalkboard')
... ).annotate(
...     whiteboard_count=Count('classroom__whiteboard')
... )
>>> q[0].chalkboard_count
6
>>> q[0].whiteboard_count
6
>>> Whiteboard.objects.count()
3
>>> Chalkboard.objects.count()
2
>>>
Also PS I really should have mentioned this is for 1.11, sorry!

Eleeleth
Jun 21, 2009

Damn, that is one suave eel.
Have you tried passing distinct=True to your Count objects? Count('model', distinct=True)

epswing
Nov 4, 2003

Soiled Meat

Eleeleth posted:

Have you tried passing distinct=True to your Count objects? Count('model', distinct=True)

Awww. Yep. That works.

https://docs.djangoproject.com/en/1.11/topics/db/aggregation/#combining-multiple-aggregations

I should have read this more carefully. Thanks!

huhu
Feb 24, 2006
Edit: nevermind.

huhu fucked around with this message at 17:09 on Jan 10, 2018

Bob Morales
Aug 18, 2006


Just wear the fucking mask, Bob

I don't care how many people I probably infected with COVID-19 while refusing to wear a mask, my comfort is far more important than the health and safety of everyone around me!

I'm writing a phpbb style clone as a learning experience in Django

In the list of Topics I'd like to show the author of the last post, and the time they made the last post.



I have something like this so far:

for each t in threads
t.title, t.author

I can't do logic in the views (rightfully so), so that prevents me from doing something like:

Posts.objects.filter(thread_id = t._id).latest(created).post
Posts.objects.filter(thread_id = t._id).latest(created).created

So do I build it all up in a context and pass it to my view, or do I just add the last Post author/created date to the Topic model itself?

Data Graham
Dec 28, 2009

📈📊🍪😋



Adding a @property method to the Thread model is how I'd do it, but assuming you have your ForeignKeys set up properly I would approach it like:

code:
@property
def latest_post(self):
    return self.post_set.order_by('-created').first()
Then you can access t.latest_post.author and whatever other attributes you want in the template. Also .first() will return None for an empty thread and you can handle it cleanly.

Bob Morales
Aug 18, 2006


Just wear the fucking mask, Bob

I don't care how many people I probably infected with COVID-19 while refusing to wear a mask, my comfort is far more important than the health and safety of everyone around me!

Data Graham posted:

Adding a @property method to the Thread model is how I'd do it, but assuming you have your ForeignKeys set up properly I would approach it like:

code:
@property
def latest_post(self):
    return self.post_set.order_by('-created').first()
Then you can access t.latest_post.author and whatever other attributes you want in the template. Also .first() will return None for an empty thread and you can handle it cleanly.

ahh that makes perfect sense - my lack of python knowledge is showing

Bob Morales
Aug 18, 2006


Just wear the fucking mask, Bob

I don't care how many people I probably infected with COVID-19 while refusing to wear a mask, my comfort is far more important than the health and safety of everyone around me!

Data Graham posted:

Adding a @property method to the Thread model is how I'd do it, but assuming you have your ForeignKeys set up properly I would approach it like:

code:
@property
def latest_post(self):
    return self.post_set.order_by('-created').first()
Then you can access t.latest_post.author and whatever other attributes you want in the template. Also .first() will return None for an empty thread and you can handle it cleanly.

What does the @property decorator do for me here? I notice it works without it.

Data Graham
Dec 28, 2009

📈📊🍪😋



Bob Morales posted:

What does the @property decorator do for me here? I notice it works without it.

It treats it as a property instead of a method; in other words in python code you can call it with t.latest_post instead of t.latest_post().

Not relevant in templates because the {{ }} syntax doesn't use parentheses. But in code, if you have a function where you can generate some output using only data inherent to the model (i.e. self), and no other parameters, then you can stick that function into the model as a @property and pretend it's just another attribute.

epswing
Nov 4, 2003

Soiled Meat
Using DRF, if I have a model that has an ImageField, in my views I have to set parser_classes = (MultiPartParser, FormParser), and the client has to deal with that API endpoint using multi-part forms. But all other models, the client uses application/json. I don't like that my API looks like "use json everywhere, except for this one, where you have to use multi-part form".

Is it typical to pull out just the ImageField (or FileField) into its own url, to keep your API consistent?

In other words, to create a Person that has a name (CharField), and an img (ImageField), the client would POST { name: 'abc' } to /api/person/ and then PUT <binarydata> /api/person/42/img/

epswing fucked around with this message at 04:31 on Jan 17, 2018

Thermopyle
Jul 1, 2003

...the stupid are cocksure while the intelligent are full of doubt. —Bertrand Russell

Bob Morales posted:

What does the @property decorator do for me here? I notice it works without it.

Just to make it clear...it only works because the way you call functions/methods in Django templates is to just put the name of the function without the parentheses. In any python code it would not work without the @property decorator.


epalm posted:

Using DRF, if I have a model that has an ImageField, in my views I have to set parser_classes = (MultiPartParser, FormParser), and the client has to deal with that API endpoint using multi-part forms. But all other models, the client uses application/json. I don't like that my API looks like "use json everywhere, except for this one, where you have to use multi-part form".

Is it typical to pull out just the ImageField (or FileField) into its own url, to keep your API consistent?

In other words, to create a Person that has a name (CharField), and an img (ImageField), the client would POST { name: 'abc' } to /api/person/ and then PUT <binarydata> /api/person/42/img/

To be sure I understand what you're asking...are you imaging some way of posting binary data via JSON?

The only way to do that is to encode the image data into text using something like b64 encoding. Otherwise the only solution is an endpoint purely for file uploads.

epswing
Nov 4, 2003

Soiled Meat

Thermopyle posted:

To be sure I understand what you're asking...are you imaging some way of posting binary data via JSON?

Naw, it's more a question of consistency.

OK, so say my API looks like this:
pre:
/api/customer GET json, POST json
/api/customer/{id} GET json, PUT json

/api/order GET json, POST json
/api/order/{id} GET json, PUT json

/api/school GET json, POST json
/api/school/{id} GET json, PUT json

/api/contact GET json, POST multipart form
/api/contact/{id} GET json, PUT multipart form
The Contact model contains an ImageField, so to POST and PUT Contacts, the client has to use multipart forms. All other models, the client uses straight json. I don't like the fact that the way my client communicates with one of my models is different just because it contains a "special" field. I.e. "don't forget, when you're creating or editing contacts, you have to use multipart form!"

So what I'm asking is, is it common to pull the "special" field out of that serializer, into a new serializer, and create a new endpoint for it, like so:

pre:
/api/customer GET json, POST json
/api/customer/{id} GET json, PUT json

/api/order GET json, POST json
/api/order/{id} GET json, PUT json

/api/school GET json, POST json
/api/school/{id} GET json, PUT json

/api/contact GET json, POST json
/api/contact/{id} GET json, PUT json
/api/contact/{id}/img GET json, PUT multipart form

Thermopyle
Jul 1, 2003

...the stupid are cocksure while the intelligent are full of doubt. —Bertrand Russell

epalm posted:

pre:
/api/contact GET json, POST json
/api/contact/{id} GET json, PUT json
/api/contact/{id}/img GET json, PUT multipart form

I'd say it's more common to have something like...

pre:
/api/contact/{id} GET json, PUT json
/api/contact/{id}/img PUT multipart form
and just return the img url when GETing JSON from /api/contact/{id}, but really either way is fine.

epswing
Nov 4, 2003

Soiled Meat

Thermopyle posted:

just return the img url when GETing JSON from /api/contact/{id}

Right, that would make sense. Though I'd want to mark it as read-only, I suppose.

Thermopyle
Jul 1, 2003

...the stupid are cocksure while the intelligent are full of doubt. —Bertrand Russell

Channels 2

Basically Channels 2 is a rewrite to take advantage of pythons native asyncio. This also means it is only compatible with python 3.5+. This doesn't seem to be much of an issue since Django is also moving in this direction.

The big conceptual change is that it more closely mirrors how traditional WSGI works; applications now ruin inside of their protocol servers.

I haven't had enough opportunities to play with Channels to really really form an opinion about if its the way forward for python + async, but I'm very glad someone is working on this.

huhu
Feb 24, 2006
Dumb question... how should I be doing this to get DRF to return the image url?

code:
It is redundant to specify `get_image_url` on SerializerMethodField 'image_url' in serializer 'LinkSerializer', because it is the same as the default method name. Remove the `method_name` argument.
code:
class LinkSerializer(serializers.ModelSerializer):
    image_url = serializers.SerializerMethodField('get_image_url')

    class Meta:
        model = Link
        fields = (
            'id',
            'name',
            'src',
            'image_url',
        )

    def get_image_url(self, obj):
        return obj.src

Thermopyle
Jul 1, 2003

...the stupid are cocksure while the intelligent are full of doubt. —Bertrand Russell

huhu posted:

Dumb question... how should I be doing this to get DRF to return the image url?

code:
It is redundant to specify `get_image_url` on SerializerMethodField 'image_url' in serializer 'LinkSerializer', because it is the same as the default method name. Remove the `method_name` argument.
code:
class LinkSerializer(serializers.ModelSerializer):
    image_url = serializers.SerializerMethodField('get_image_url')

    class Meta:
        model = Link
        fields = (
            'id',
            'name',
            'src',
            'image_url',
        )

    def get_image_url(self, obj):
        return obj.src

Just do what the error message is telling you: image_url = serializers.SerializerMethodField()

DRF does a few things I don't like and this is an example of that. SerializerMethodField will automatically look for a method with the field name prepended with get_.

Data Graham
Dec 28, 2009

📈📊🍪😋



Yeah, “magic” convenience behaviors save time for experts but they are absolute hell to reverse engineer.

Plus it seems un-Pythonic.

Adbot
ADBOT LOVES YOU

epswing
Nov 4, 2003

Soiled Meat
Anyone know how to clear an image with Django 1.11 and DRF 3.7?

https://stackoverflow.com/questions/48676365/how-can-i-clear-an-image-with-django-rest-framework

I'm sending in None and empty-string, and getting errors both ways.

  • 1
  • 2
  • 3
  • 4
  • 5
  • Post
  • Reply