Today projects, especially at the initial stage, are built from ready-made blocks. For example, smart hosting or a service of quick sending messages to the browser to the user. By folding such blocks in different ways, you can get a completely unexpected result. And the more different blocks available to you, the more diverse projects can be obtained. Sometimes there are blocks that can replace several other blocks at once. And these blocks themselves are the same projects consisting of other blocks.

How easy is it now to make such a service as image hosting? In principle, it was easy to do it before. But progress does not stand still, and now more nuances can be taken into account for the same time. I have already
talked about the project Uploadcare. This is a service that makes it easier to work with files: download, store, process and distribute to the end user. We will use it as the main unit.
The example will be written in Python. Firstly, because I know Python best of all, and secondly, the
pyuploadcare library
is updated first. In fact, for Uploadcare there are libraries for different languages, and all of them are in open source. If there is no functionality in the module you need, you can wait for it to appear, or add it yourself.
')
Start by creating a new project on Django:
$ pip install django pyuploadcare==0.19 $ django-admin.py startproject upload_test $ cd upload_test/ && chmod u+x ./manage.py $ ./manage.py startapp imageshare $ ./manage.py syncdb --noinput
In settings.py, in addition to the usual database connection settings and INSTALLED_APPS, you need to specify a public and private key:
UPLOADCARE = { 'pub_key': 'demopublickey', 'secret': 'demoprivatekey', }
For the demonstration, I will use a demo account, which in itself already seems logical. The only limitation of this account is that the files themselves are deleted after some time.
The project will be very small: on the main page will be a form for download. After it is sent, the picture ID will be saved to the database. For this, such a model would be enough:
import string import random from pyuploadcare.dj import ImageField from django.db import models class Image(models.Model): slug = models.SlugField(max_length=10, primary_key=True, blank=True) image = ImageField(manual_crop="") def save(self, *args, **kwargs): if not self.slug: self.slug = ''.join(random.sample(string.ascii_lowercase, 6)) super(Image, self).save(*args, **kwargs)
As you can see, ImageField here is not jung, but from the pyuploadcare package. I have specified only one setting: it will allow the user to select the area of the image that he wants to upload. The save () method generates a slug for a short link.
Now the beautiful: a view for the main page that saves the image, and a view that allows you to view it:
from django.views.generic import DetailView from django.views.generic.edit import CreateView from .models import Image class UploadView(CreateView): model = Image class ImageView(DetailView): model = Image
Class, 2 lines each. Just Django is also a very high-quality unit for your projects. For the form to be displayed, you need a small template that will display it. Nothing special, but you need to specify the public key for the widget and do not forget to put {{form.media}} in the document tag. Many people forget about this attribute.
{% extends "base.html" %} {% block head %} <script> UPLOADCARE_PUBLIC_KEY = 'demopublickey'; </script> {{ form.media }} {% endblock %} {% block body %} <div class="center"> <form action="." method="post"> <p>Please, select an image:</p> <p>{{ form.image }}</p> <p><input type="submit" value="send"></p> {{ form.errors }} </form> </div> {% endblock %}
We start.

Widget with a choice of files appeared on the page. But saving does not work, Janga swears: “No URL to redirect to”. It is understandable, you need to specify somewhere how to get the full link to the picture. Add another method to the model.
@models.permalink def get_absolute_url(self): return 'detail', (), {'pk': self.pk}
It remains to write a template for a complete withdrawal and we can say that the goal has been achieved.
{% block body %} <img src="{{ image.image }}"> {% endblock %}
The guys from my instagram say hello.

The attentive reader will notice that everything about everything took a maximum of 15 minutes. Wtf, how do we take another quarter of an hour?
You can improve the download page. In its current form, the user has to make two extra clicks: to open the widget and to send the form. You can remove them. To do this, use the
javascript widget api :
<script> (function() { uploadcare.start(); var widget = uploadcare.Widget('#id_image'); widget.openDialog(); widget.onChange(function(file) { if (file) { var form = document.getElementById('upload-form'); form.submit(); form.style.display = 'none'; } }); })(); </script>
Here we initialize the widget using the start () method, without waiting for the page to load, and then open the dialog, without waiting for the user to click. Well, if the file was uploaded, send the form.
What else? You can make a little more informative image viewing page: show thumbnail instead of full image and display some information.
{% block body %} <h2>Uploaded Image</h2> <a href="{{ image.image.cdn_url }}"> <img align="left" src="{{ image.image.cdn_url }}-/stretch/off/-/resize/260x/"></a> <div class="float-info"> <p> <b>Filename</b>: {{ image.image.info.filename }}<br> <b>Uploaded</b>: {{ image.image.info.datetime_uploaded|slice:":10" }}<br> <b>Original size</b>: {{ image.image.info.size|filesizeformat }}<br> </p> <p><a href="{{ image.image.cdn_url }}">Full link</a></p> </div> <br clear="left"> <p><a href="{% url 'index' %}">Upload another image</a></p> {% endblock %}
The thumbnail of the desired size is obtained by specifying options directly in the url of the image. Information is obtained through the info method. Unfortunately, datetime_uploaded is passed as a string, so I had to cheat - cut out the first 10 characters. In an amicable way, it was necessary to parse it. I hope someone will correct it before the ten thousandth year :)

Another little thing that can be fixed is to properly handle the situation when the picture was deleted. Correctly processing means giving an error 404 instead of 500. The best thing to do when getting an object from the database is to request information about the file and, if it shows that the file has been deleted, delete the link stored in us. In addition, if the file is deleted long enough, api may not return anything at all. Need processing and such a case.
class ImageView(DetailView): model = Image def get_object(self): object = super(ImageView, self).get_object() try: if object.image.is_removed: raise ValueError('File was deleted.') except (InvalidRequestError, ValueError): object.delete() raise Http404 return object
Now, perhaps, you can stop. It remains to use the last block - heroku hosting free for a certain load - and see the result:
iamshare.herokuapp.com .
The source code is also available, if someone is interested to look at everything together.