ahmad
ahmad

Reputation: 31

Django Admin Foreign Key Error on Djongo database engine

I'm using djongo package for a database backend engine in order to connect to MongoDB and define my models on it.

settings.py:

DATABASES = {
  # 'default': {
  #   'ENGINE': 'django.db.backends.sqlite3',
  #   'NAME': str(BASE_DIR / 'db.sqlite3'),
  # },

  'default': {
    'ENGINE': 'djongo',
    'NAME': 'djongo-db',
    'ENFORCE_SCHEMA': False,
    'CLIENT': {
      'host': 'localhost',
      'port': 27017,
      'username': 'root',
      'password': 'root',
      'authSource': 'admin',
      'authMechanism': 'SCRAM-SHA-1'
    }
  }
}

models.py:


class EventModel(BaseModel)
    name = models.CharField(max_length=20)

class CalendarModel(BaseModel):
    name = models.CharField(max_length=20)
    color = models.CharField(max_length=20)

    event = models.ForeignKey(to=EventModel, on_delete=models.SET_NULL, null=True)

and admin.py:

from django.contrib import admin
from .models import CalendarModel, EventModel


@admin.register(CalendarModel)
class CalendarAdmin(admin.ModelAdmin):
    exclude = ['_id']

@admin.register(EventModel)
class EventAdmin(admin.ModelAdmin):
    exclude = ['_id']

It works fine with using SQLite backend and it's working when djongo backend without foreign key field but gives me an error when using the djongo backend and has foreign key field. It said:

error image

As you can see in the image above, it can load objects from the database and detects the relation correctly, but it can't save it.

And I can't create a new object with relation to another object. How I can fix this?

Update


I can create objects using code like this, the problem seems to be from Django admin site

e = EventModel.objects.first()
CalendarModel.objects.create(name="test", color="red", event=e)

Upvotes: 3

Views: 534

Answers (1)

zN3utr4l
zN3utr4l

Reputation: 21

I noticed that when you try to save an instance of an object with PK an ObjectId that it is transformed into a string and consequently no longer corresponds to an instance of an object, so with the override of the get_form method in POST you can intercept this data and change the string to ObjectId, but as you can see in the Django documentation:

The QueryDicts at request.POST and request.GET will be immutable when accessed in a normal request/response cycle.

so you can use the recommendation from the same documentation:

To get a mutable version you need to use QueryDict.copy()

or ... use a little trick, for example, if you need to keep a reference to an object for some reason or leave the object the same:

# remember old state
_mutable = data._mutable

# set to mutable
data._mutable = True

# сhange the values you want
data['param_name'] = 'new value'

# set mutable flag back
data._mutable = _mutable

where data it is your QueryDicts

In this case:

@admin.register(CalendarModel)
class CalendarModelAdmin(admin.ModelAdmin):
    list_display = ('....')
    ....

    def get_form(self, request, obj, change, **kwargs):
        if request.POST:
            # remember old state
            _mutable = request.POST._mutable
            # set to mutable
            request.POST._mutable = True
            # сhange the values you want
            request.POST['event'] = ObjectId(request.POST['event'])
            # set mutable flag back
            request.POST._mutable = _mutable

        return super().get_form(request, obj=obj, change=change, **kwargs)

Upvotes: 1

Related Questions