Here's how I'd start to tackle this challenge with the use of JSONField (and PostgreSQL). It's just to give you a basic idea and I tried to follow Django principles. First two simple models. One to store model definitions as defined by users and one to store data.
models.py
from django.db import models
from django.contrib.postgres.fields import JSONField
from django.utils.text import slugify
class ModelDefinition(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
verbose_name = models.CharField(max_length=50)
name = models.CharField(max_length=50, unique=True)
definition = JSONField()
def save(self, *args, **kwargs):
self.name = slugify(self.verbose_name)
super().save(*args, **kwargs)
class Data(models.Model):
model_definition = models.ForeignKey(ModelDefinition, on_delete=models.CASCADE)
data = JSONField()
Below is an example how you'd add model definitions, add and return data. I didn't include data validation since it would have taken me at least an hour to write code for basic validation.
views.py
from .models import ModelDefinition, Data
from django.http import HttpResponse, JsonResponse
def create_model_definition(request):
''' Handles creation of model definitions '''
model_definition = {
'fields': [
{
'name': 'automobile',
'verbose_name': 'Automobile',
'data_type': 'string',
'max_length': 50,
'blank': False,
'null': False
},
{
'name': 'type',
'verbose_name': 'Automobile type',
'data_type': 'string',
'max_length': 20,
'blank': False,
'null': False
},
{
'name': 'doors',
'verbose_name': 'Number of doors',
'data_type': 'integer',
'blank': False,
'null': False
}
],
'global_options': {
'guest': {
'verbose_name': 'Allow guests to enter data',
'option': True
},
'public': {
'verbose_name': 'Data is publicly accessible',
'option': False
}
}
}
ModelDefinition.objects.create(
user=request.user,
verbose_name='My automobiles',
definition=model_definition
)
return HttpResponse('model definition created')
def add_data(request, table='my-automobiles'):
''' Handles data entry '''
model_definition = get_object_or_404(ModelDefinition, user=request.user, name=table)
if not request.user.is_authenticated and not model_definition.definition['global_options']['guest']['option']:
return HttpResponse('Sorry, only authenticated users can enter data')
data_rows = [
{
'automobile': 'Audi',
'type': 'limousine',
'doors': 4
},
{
'automobile': 'Fiat',
'type': 'hatchback',
'doors': 3
},
{
'automobile': 'Iveco',
'type': 'truck',
'doors': 2
}
]
for row in data_rows:
Data.objects.create(
model_definition=model_definition,
data=data
)
return HttpResponse('rows saved')
def show_data(request, table='my-automobiles'):
''' Returns data in JSON format '''
model_definition = get_object_or_404(ModelDefinition, name=table)
if not request.user.is_authenticated and not model_definition.definition['global_options']['public']['option']:
return HttpResponse('Sorry, only authenticated users can view data')
data = Data.objects.filter(model_definition__name=table)
return JsonResponse(data)
Well, that's how I'd start working on this, no idea what I'd end up with. Of course, I haven't tested any of this code :)