0

I'm building REST API application using Django Rest Framework. Through the post request i'm receiving data and save it with citizen_id and import_id unique_together (see the models.py). Import_id for every saving is calculating via get_current_import_id() function (see views.py) For concurrent requests i'm using "curl1 & curl2" command Probably i don't understand some basics, but what i want is to save every concurrent request (every new request should be with previous import_id + 1). Also i'm using a "current_import_id" value in serializers for create and saving instances.

P.S PATCH and GET requests are working with concurrent data. For example two concurrent PATCH requests are randomly saving (sometimes "first" is first, sometimes "first" is second, but it works)

POST request example, JSON

{
 "citizen_id": 1,
 "name": "Foo",
}

models.py

class CitizenInfo(models.Model):
    citizen_id = models.PositiveIntegerField()
    import_id = models.PositiveIntegerField()
    name = models.CharField(max_length=256)

    class Meta:
        unique_together = ('import_id', 'citizen_id',)

views.py Getting the maximum import_id in database and determine current_import_id

def get_current_import():
    previous_import_id = CitizenInfo.objects.aggregate(
                                     Max('import_id'))['import_id__max']
    if not previous_import_id:
        previous_import_id = 0
    current_import_id = previous_import_id + 1
    return current_import_id

At the moment the result statuses i getting from POST "curl1 & curl2" command are 201 and 500 (sometimes 500 and 201 due to randomness).

By the way, i've tried django F() and select_for_update(nowait=True) but it doesn't help as it expected

Project configuration: Django + Gunicorn + Nginx

Any advise, please.

Dm_sm
  • 1
  • 3
  • How did you deploy your Django instance? What WSGI server are you using? The development server is not made for concurrency and must not be used in production. – Klaus D. Aug 25 '19 at 04:41
  • DJANGO + Gunicorn + nginx – Dm_sm Aug 25 '19 at 05:03
  • How are you managing transactions? Can you share your `F()` implementation? That is usually how concurrency issues like this are handled. I think you should probably use `nowait=False` if you're using `select_for_update` since otherwise the request will fail if `select_for_update` cannot acquire the lock. And ensure your database supports `select_for_update`. Finally any reason you can't use an `AutoField` with `pk=False`? https://docs.djangoproject.com/en/2.2/ref/models/fields/#autofield – azundo Aug 25 '19 at 05:32
  • Instead of an expensive aggregate use `order_by()` and `first()`. Also make sure your column is indexed. – Klaus D. Aug 25 '19 at 06:21
  • @azundo, thank you, i solve this problem changing my models.py as you said. – Dm_sm Aug 25 '19 at 17:57
  • @Klaus D. I decided to use `Model.objects.latest('field').field` What do you think? – Dm_sm Aug 25 '19 at 18:13

1 Answers1

0

Thanks to @azundo, i changed my models.py and a bit logic and now it works.

class ImportId(models.Model):
    import_id = models.AutoField(primary_key=True)


class CitizenInfo(models.Model):
    citizen_id = models.PositiveIntegerField()
    import_id = models.ForeignKey(ImportId, on_delete=models.CASCADE)
    name = models.CharField(max_length=256)

But also i faced

the desirable behaviour

of AutoField, as it says in another question Django model instances primary keys do not reset to 1 after all instances are deleted. But i think it's ok for my case.

Dm_sm
  • 1
  • 3