3

When attempting to generate QR codes using QR-code 5.3 using Python3.4. I'm confronted with several issues:

At first I used io.StringIO and I got a string argument expected, got 'bytes' error message. So I then changed io.StringIO to io.BytesIO. Then I got another error which was '_io.BytesIO' object has no attribute 'len' so in order to get the length of the object I used buffer.getbuffer().nbytes but now I'm getting a maximum recursion depth exceeded and it's generating 298 QR code images instead of just one. Any ideas what I am doing wrong?

from django.db import models
from django.conf import settings
from django.core.urlresolvers import reverse
from django.core.files.uploadedfile import InMemoryUploadedFile

import random
import qrcode
import io
import sys

from PIL import Image

import pdb;


def qrcode_location(instance, filename):
    return '%s/qr_codes/%s' % (instance.user.username, filename)


# Create your models here.
class EmployeeProfile(models.Model):
    user = models.ForeignKey(settings.AUTH_USER_MODEL,  on_delete=models.CASCADE)
    qrcode = models.ImageField(upload_to=qrcode_location, null=True, blank=True)
    first_name = models.CharField(max_length=30)
    last_name = models.CharField(max_length=30)

    def __str__(self):
        return self.first_name + ' ' + self.lastname

    def save(self):
        first_initial = self.first_name[0].upper()
        second_initial = self.last_name[0].upper()
        id_number = first_initial + second_initial + str(random.randint(1000000, 9999999))
        self.generate_qrcode()

        if not EmployeeProfile.objects.filter(employee_id=id_number).exists():
            self.employee_id = id_number
            super(EmployeeProfile, self).save()

    def generate_qrcode(self):
        qr = qrcode.QRCode(
            version=1,
            error_correction=qrcode.constants.ERROR_CORRECT_L,
            box_size=10,
            border=4,
        )
        qr.add_data('Some data')
        qr.make(fit=True)

        img = qr.make_image()

        buffer = io.BytesIO()
        img.save(buffer)
        filename = 'qrcode.png'
        filebuffer = InMemoryUploadedFile(buffer, None, filename, 'image/png', buffer.getbuffer().nbytes, None)
        self.qrcode.save(filename, filebuffer)

--------------------SOLUTION UPDATE-------------------------------

Since save was calling generate_qrcode, and that was calling self.qrcode.save and the model was calling save causing infinite recursion. So to prevent that you just need to bypass it by providing an additional third argument to the FileField's save method.

Django FileField in model maximum recursion depth exceeded while calling a Python object

# set 3 argument to false(save=False) otherwise infinite recursion will happen
self.qrcode.save(filename, filebuffer, False)
4
  • What happens when you step through pdb? Also, it's a stab in the dark, but does the recursion go away if you remove self.qrcode.save? Commented May 26, 2016 at 19:34
  • What changes when you remove super(EmployeeProfile, self).save()? Commented May 26, 2016 at 19:41
  • @AlexHall, Yes, the recursion does go away when I remove self.qrcode.save, but then it doesn't save the generated images. Commented May 26, 2016 at 19:42
  • @Keiwan, Nothing changes, the result is the same Commented May 26, 2016 at 19:43

2 Answers 2

7

self.qrcode.save means that the whole model object needs to be saved, so it leads to a call to save which calls generate_qrcode which calls self.qrcode.save... (by the way you should be able to see this in the traceback) so your problem has nothing to do with BytesIO. Insert a condition somewhere to break the recursive loop.

Sign up to request clarification or add additional context in comments.

2 Comments

So you're right. Since Save is calling generate_qrcode, and it's calling self.qrcode.save and Then save is being called, it keeps looping. So where should I call generate_qrcode if not in the save() method?
2

okey here is my full solution for models.Model who works with slug field and also get_absolute_url and qr_code

class Posts(models.Model):
    slug = models.SlugField(unique=True)
    title = models.CharField(max_length=30)
    my_qrcode = models.ImageField(upload_to='qrCode', null=True, blank=True)

    def save(self, *args, **kwargs):
        # we override the save method other wise, slug will not be effect and get_absolute_url will not work
        if self.slug:
            pass # here prevents to create slug again when updating your posts
        else:
            self.generate_qrcode()
        super(Posts,self).save(*args, **kwargs)

    def get_absolute_url(self):
        return reverse('posts:detail', kwargs={'slug':self.slug})

    def generate_qrcode(self):
        # this part creates unique slugs
        slug = slugify(self.title)
        while self.__class__.objects.filter(slug=slug).order_by('-id').exists():
            qs = self.__class__.objects.filter(slug=slug).order_by('-id')
            new_slug = '%s-%s' % (slug, qs.first().id)
            slug = new_slug
        self.slug = slug

        qr = qrcode.QRCode(
            version=1,
            error_correction=qrcode.constants.ERROR_CORRECT_L,
            box_size=6,
            border=0,
        )
        qr.add_data(self.get_absolute_url())
        qr.make(fit=True)

        img = qr.make_image()

        buffer = StringIO.StringIO()
        img.save(buffer)
        filename = 'QrCode-%s.png' % (slug)
        filebuffer = InMemoryUploadedFile(buffer, None, filename, 'image/png', buffer.len, None)
        self.my_qrcode.save(filename, filebuffer, False) # we already have save method so just make False itself save behavior

just it, benefit of helping.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.