0

Django's ImageField lets me store a file object in it using a normal assignment.

from urllib import request
from django.db import models
from django.core.files.base import ContentFile

class Customer(models.Model):
    logo = models.ImageField()

customer.logo = ContentFile(request.urlopen(url), 'image.png')

Now I want to inherit a custom field type from ImageField. It takes an URL as a plain string instead of a file object for assignment. Internally, it should fetch the image and assign it to it's base class, right as in the manual example above.

from django.db import models

class UrlImageField(models.ImageField):
    def __set__(self, instance, value):
        super() = ContentFile(urllib.request.urlopen(value), 'image.png')

Therefore, I need to call the assignment operator on the ImageField base class. How can I do that in Python?

danijar
  • 32,406
  • 45
  • 166
  • 297
  • So what you want is create a field that fetches a remote URL and saves it as an ImageField, right? – Renan Ivo Jul 24 '14 at 17:44
  • What makes you think that Django is doing anything fancy with the assignment (hint: it isn't)? `image` is simply a name that first refers to the object created by `models.ImageField()`, then refers to the object created by `ContentFile`. That's all. – chepner Jul 24 '14 at 17:47
  • @chepner How does the `ContentFile` get stored in the database then? It though I would need a `ImageField` instance for this. – danijar Jul 24 '14 at 22:47
  • 1
    I'm not sure, but presumably it would work even if you did not assign the results of either command to `image`. My guess is that Django maintains some hidden references that are used. – chepner Jul 24 '14 at 23:14

2 Answers2

2

The assignment operator cannot be overloaded.

  • How do they do that with Django's `ImageField` then? – danijar Jul 24 '14 at 17:16
  • Can you explain what you mean by that? If I'm not mistaken, you're looking to overload the `=` python operator. That operator is intrinsic to the language and cannot be overloaded. –  Jul 24 '14 at 17:21
  • 2
    @danijar Note that `x = something` is *different* from `x.attribute = something`. The first one **cannot** be overloaded, the latter can, in several ways. Also code inside classes is special, because you have some control over it using metaclasses (at least in python3.4+), while other code doesn't give that control. – Bakuriu Jul 24 '14 at 17:31
  • 2
    In fact, `=` is not an operator; it is part of the assignment statement which is defined by the Python grammar, and is no more modifiable than an `if` statement. – chepner Jul 24 '14 at 17:44
  • @AshishKasturia Right, I though that `ImageField` would have to overload the `=` operator to make it possible to assign a `ContentFile` to it. Since that is not true, can you explain how this assignment works then? Are the two types related somehow? – danijar Jul 24 '14 at 22:51
  • 1
    The field probably has a getter and setter associated with it. As @Bakuriu mentioned, the attribute `=` can be overloaded. Take a look at this url to find out more - http://stackoverflow.com/questions/2898711/django-model-fields-getter-setter –  Jul 24 '14 at 23:19
  • @Bakuriu Could you please take a look at the code in my question? I just updated it a bit, because I was actually talking about the `x.attributes = something` form. Just didn't understand the concept right. – danijar Jul 25 '14 at 09:24
1

You don't want to (and cannot) overload assignment, but you simply want to extend the ImageField descriptor. Descriptors are explained in the Descriptor HowTo Guide in depth. You should also take a look at: Understanding __get__ and __set__ and Python descriptors

The essentials are that they define __set__ and __get__ methods that control the attribute access. These methods are just normal methods, so you'll have to extend them exactly as normal methods:

from django.db import models

class UrlImageField(models.ImageField):
    def __set__(self, instance, value):
        super().__set__(instance, ContentFile(urllib.request.urlopen(value), 'image.png'))
Community
  • 1
  • 1
Bakuriu
  • 98,325
  • 22
  • 197
  • 231
  • Thanks a lot, think I understand this now. However, it still [doesn't quite work](http://stackoverflow.com/questions/24957820/how-to-derive-a-custom-field-from-djangos-imagefield). Do you have an idea? – danijar Jul 26 '14 at 00:29