I have a simple model and I want to save it using the ModelForm.
Here's the code:
#models.py
class MyArchive(models.Model):
archive_id = models.CharField(max_length = 20, primary_key=True)
description = models.CharField(max_length = 50, blank = True)
archive_file = models.FileField(upload_to = "my_archives/")
#views.py
class MyArchiveForm(ModelForm):
class Meta:
model = MyArchive
def upload(request):
if request.method == 'POST':
form = MyArchiveForm(request.POST, request.FILES)
if form.is_valid():
form.save()
return HttpResponse('uploaded success!')
else:
logger.debug("invalid form")
return HttpResponse('upload fail!')
I've synced DB and saw the tables created. But every time it goes to form.save then says
DatabaseError, no such table.
Is my way to save using ModelForm wrong?
UPDATE:
What's even weird that when I removed this line: form.save(), it fails at if form.is_valid() with the same error no such table, but when I run django in debug mode, if form.is_valid() works fine.
It's a little trick, turns out led by bad database configuration, I was using relative path in settings.py, so it didn't find the db file at running time, but when run django sycdb, it can find where the db loc.
'default' : {
'ENGINE': 'django.db.backends.sqlite3', # Add 'postgresql_psycopg2', 'mysql', 'sqlite3' or 'oracle'.
'NAME': 'sqlite.db', # Or path to database file if using sqlite3.
}
UPDATE:
To use relative path on db file, I should have db setting like this:
from os.path import dirname, join
PROJECT_DIR = dirname(__file__)
DATABASES = {
# ...
'NAME': join(PROJECT_DIR, 'sqlite.db'),
# ...
}
see Can I make the Django database path (for sqlite3) "cross-platform"?
Related
I am trying to use multiple different DB in a single app(todo). I am using Djongo package for dealing mongodb.
Settings.py
DATABASES = {
'default':{},
'sql_db': {
'ENGINE': 'django.db.backends.postgresql_psycopg2',
'NAME': 'my_db',
'USER': '******',
'PASSWORD': '***',
'HOST': 'localhost',
'PORT': '5432',
},
'mongodb':{
'ENGINE': 'djongo',
'NAME': 'mongo_db'
}
}
todo/models.py
class Task(models.Model):
todo = models.CharField(max_length=200)
status = models.BooleanField(default=False)
def __str__(self):
return self.todo
todo/serializers.py
class TodoSerializer(serializers.ModelSerializer):
class Meta:
model = Task
fields = '__all__'
todo/views.py
#api_view(['POST'])
def todoCreate(request):
serializer = TodoSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
serializer.save(using='mongodb')
return Response(serializer.data)
it successfully saved the record into 'sql_db' but not save in the 'mongodb'.
My suggestion is to put the dual database saving into a save function in models.py:
class Task(models.Model):
...
def save(self, *args, **kwargs):
super(Task, self).save(using='sql_db')
super(Task, self).save(using='mongodb')
and drop the 2nd save in the view.
This is so any save of a Task object will trigger the dual save (E.g. from the django admin or another script) rather than just when it comes from this one serializer view.
I'm not sure the serializer.save method passes the 'uses' through to the objects save function which may be the problem for you. If you want to do it in the serializer then you may have to override the create and save in the TodoSerializer: see Serializers
For dealing with module named Djongo for connecting Django with MongoDb , one should be completely aware of the modules versions being used in the environment.
asgiref==3.5.0,
Django==4.0.3,
djongo==1.3.6,
dnspython==2.2.1,
pykerberos==1.2.4,
pymongo==3.12.1,
python-snappy==0.6.1,
pytz==2022.1,
sqlparse==0.2.4,
configure the Database settings in the settings.py. Its not necessary to use host while connnecting to the localhost of MongoDb.
DATABASES = {
'default': {
'ENGINE': 'djongo',
'NAME': 'your-db-name',
}
}
I am using two databases in my django project:
settings.py
DATABASES = {
"default": {
"ENGINE": "django.db.backends.sqlite3",
"NAME": os.path.join(BASE_DIR, "db.sqlite3"),
},
"2ndDB": {
"ENGINE": "sql_server.pyodbc",
"NAME": "2nddb-sqldb",
"USER": CRED["user"],
"PASSWORD": CRED["password"],
"HOST": "2ndserver-sqlsrv.database.windows.net",
"PORT": 1433,
"OPTIONS": {"driver": "ODBC Driver 17 for SQL Server",},
},
In my view I try to validate my form:
views.py
def registration(request):
if request.method == "POST":
form = RegistrationForm(request.POST, user=request.user.get_username())
if form.is_valid():
forms.py
class RegistrationForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
self.helper = FormHelper()
self.helper.form_method = "post"
self.helper.add_input(Submit("submit", "OK"))
class Meta:
model = Registration
fields = (
"uuid",
"xyz",
)
Which fails as it tries to validate the unique constraint of the pk of the model in the unmanaged db:
models.py
class Registration(models.Model):
uuid = models.UUIDField(primary_key=True, verbose_name="UUID")
class Meta:
db_table = "xyz"
managed = False
It is pretty that it tries to access the primary db an obviously can not find the tabel. (Which is the error that I receive.) I know how to use the second DB when saving the form or making queries by using the "using" option. I can't figure out how to make this work with the validation though.
I misread you're question. What you are looking for is a database router: https://docs.djangoproject.com/en/3.0/topics/db/multi-db/#automatic-database-routing
You configure these to route some requests to your second database. You could use the router show in the django docs but instead of route_app_labels = {'auth', 'contenttypes'} you would use: route_app_labels = {'yourapp', 'registration'} (and change the relevant reference to your DB too).
Using Django Rest Framework, how to execute update/PUT calls to a non-standard database?
In my Django project I'm using a separate database for the application data:
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
},
'bookstore': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'bookstore.sqlite3'),
}
}
I use the ModelSerializer and ModelViewSet to automatically create the API for me. I loop over the models to automatically generate all the Serializers and ViewSets (quite a big number of tables), the generated classes end up looking like this:
ModelSerializer:
class BookSerializer(serializers.ModelSerializer):
class Meta:
model = Book
fields = '__all__'
ModelViewSet:
class BookViewSet(viewsets.ModelViewSet):
queryset = Book.objects.using('bookstore').all()
serializer_class = BookSerializer
I needed to add the using('bookstore') here to make this work.
Reading (GET) works fine. When I try to do a PUT update, I get a no such table: book Sqlite error.
It seems like the update isn't routed to the bookstore database.
For completeness, I use these loops to generate the ModelSerializer and ModelViewSets from the models:
ModelSerializer, generator:
models = dict(apps.all_models['api'])
for name, model in models.items():
class_name = name[:1].upper() + name[1:] + 'Serializer'
Meta = type('Meta', (object, ), {'model': model, 'fields': '__all__'})
print(class_name)
globals()[class_name] = type(class_name, (serializers.ModelSerializer,), {'Meta':Meta, })
ModelViewSet, generator:
models = dict(apps.all_models['api'])
for name, model in models.items():
capitalized_class_name = name[:1].upper() + name[1:]
viewset_class_name = capitalized_class_name + 'ViewSet'
serializer_class_name = capitalized_class_name + 'Serializer'
globals()[viewset_class_name] = type(viewset_class_name, (viewsets.ModelViewSet,), {'queryset': model.objects.using('bookstore').all(), 'serializer_class': globals()[serializer_class_name],'filter_backends': [filters.OrderingFilter], })
I designed and built a Django 1.6.2 survey application using a SessionWizardView which is connected to a MySQL database.
The problem is that (as far as I can see) the submitted form data is not getting saved to the database. This is my first time building an application like this or even working with a database.
Could someone take a look at what I have done and my code and point out any mistakes I have made as to why I cannot see any content submitted by my survey form?
The last time I posted a similar question I was told I needed to create a Model for the data and it was suggested I use a ModelForm to create the table and columns in the database. I have done this but I am still not seeing my submitted content
My Process
I created the database in MySQL (Ver 14.14 Distrib 5.6.20) via
Terminal CREATE database django_db; and the tables in it are created
when I run the command python manage.py syncdb.
I can complete my survey both on my local machine and on the public server. No errors and it appears everything works fine
I have setup phpMyAdmin and can see the django_db database and survey_person model. However I can not seem to find any of the data that should be submitted by the form.
I have tried to use the search facility in phpMyAdmin to find any of the form data I submitted but cannot see it
I have exported the database as a .CSV file but it is empty.
If I use the insert facility in phpMyAdmin the data gets saved in the DB and I can return it when I use the search facilities. The same data is also in the CSV file when I export it.
This seem to suggest that I am missing a step somewhere in my application when it comes to submitting content to the DB.
Can anyone tell me where I am going wrong?
My Code
I have tried to limit the below to only relevant code
settings.py
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'django_db',
'USER': 'root',
'PASSWORD': 'xxxxxxxxxxxxxxxxx',
'HOST': '127.0.0.1',
#'PORT': '',
}
}
urls.py
url(r'^surveyone/$', SurveyWizardOne.as_view([
SurveyFormA,
SurveyFormB,
SurveyFormC,
....
....
SurveyFormG,
SurveyFormH,
SurveyFormI
])),
forms.py
class SurveyFormA(forms.ModelForm):
birthdate = forms.DateField(widget=extras.SelectDateWidget(years = range(1995, 1900, -1)), required=False)
class Meta:
model = Person
fields = ['sender', 'birthdate', 'sex', 'relationship', 'state']
class SurveyFormB(forms.ModelForm):
class Meta:
model = Person
fields = ['internet_usage', 'smart_phone_ownership', 'smart_phone_usage']
widgets = {'internet_usage' : RadioSelectNotNull,
'smart_phone_ownership' : RadioSelectNotNull,
'smart_phone_usage' : RadioSelectNotNull,
}
class SurveyFormC(forms.ModelForm):
class Meta:
model = Person
fields = ['education', 'wages', 'presentage_savings', 'occupation', 'living']
widgets = {'education' : forms.RadioSelect,
'wages' : forms.RadioSelect,
'presentage_savings' : forms.RadioSelect,
'occupation' : forms.RadioSelect,
'living' : forms.RadioSelect,}
....
....
models.py
sender = models.EmailField(null=True, blank=True, verbose_name='What is your email address?')
birthdate = models.DateField(null=True, blank=True) #overwritten in forms.py so passed no more arguments
SEX = (
('MALE', 'Male'),
('FEMALE', 'Female'))
sex = models.CharField(null=True, blank=True, max_length=100, choices=SEX, verbose_name='What sex are you?')
RELATIONSHIP = (
('SINGLE', "Single"),
('INARELATIONSHIP', "In a relationship"),
('MARRIED', "Married"),
('DIVORCED', "Divorced"),
('SEPARATED', "Separated"),
('WIDOWED', "Widowed"),)
relationship = models.CharField(null=True, blank=True, max_length=100, choices=RELATIONSHIP, verbose_name='What is your relationship status?')
....
....
def __unicode__(self):
return self
views.py
My views.py are the most complex part of my application. Not sure if it necessary to show any but I thought just in case
class SurveyWizardOne(SessionWizardView):
def get_context_data(self, form, **kwargs):
context = super(SurveyWizardOne, self).get_context_data(form, **kwargs)
step = int(self.steps.current)
if step == 0:
self.request.session['path_one_images'] = ['P1D1.jpg', 'P2D2.jpg', 'P3D3.jpg', 'P4D4.jpg', 'P5D5.jpg', 'P6D6.jpg', 'P7D7.jpg', 'P8D8.jpg', 'P9D9.jpg']
self.request.session['instruction_task_one_images'] = ['IT1A.jpg', 'IT1B.jpg', 'IT1C.jpg']
self.request.session['instruction_task_two_images'] = ['IT2A.jpg', 'IT2B.jpg', 'IT2C.jpg']
self.request.session['images'] = []
self.request.session['slider_DV_values'] = []
PATH_ONE_IMAGES = self.request.session.get('path_one_images', [])
images = self.request.session.get('images', [])
slider_DV_values = self.request.session.get('slider_DV_values', [])
INSTRUCTION_TASK_ONE_IMAGES = self.request.session.get('instruction_task_one_images', [])
INSTRUCTION_TASK_TWO_IMAGES = self.request.session.get('instruction_task_two_images', [])
if step in range (0, 27):
self.request.session['path_one_images'] = PATH_ONE_IMAGES
self.request.session['images'] = images
self.request.session['slider_DV_values'] = slider_DV_values
self.request.session['instruction_task_one_images'] = INSTRUCTION_TASK_ONE_IMAGES
self.request.session['instruction_task_two_images'] = INSTRUCTION_TASK_TWO_IMAGES
if step == 0:
instruction_task_first_image = random.choice(INSTRUCTION_TASK_ONE_IMAGES)
context['display_image'] = instruction_task_first_image
elif step == 1:
instruction_task_second_image = random.choice(INSTRUCTION_TASK_TWO_IMAGES)
context['display_image'] = instruction_task_second_image
elif step == 9:
first_image = random.choice(PATH_ONE_IMAGES)
PATH_ONE_IMAGES.remove(first_image)
context['display_image'] = first_image
images.insert(0, first_image)
self.request.session['first_image'] = images[0]
self.request.session.get('first_image')
elif step == 10:
second_image = random.choice(PATH_ONE_IMAGES)
PATH_ONE_IMAGES.remove(second_image)
....
....
return context
def done(self, form_list, **kwargs):
return render(self.request, 'Return_to_AMT.html', {
'form_data': [form.cleaned_data for form in form_list],
})
phpMyAdmin
A screenshot of the DB from phpMyAdmin. NOTE: Not the hidden fields at the start are for introduction steps in the survey form, code not shown here for brevity.
Submitting data to a model form does not cause it to be saved automatically. If you want save data from a model form to the database, you need to call its save() method. You could do this in the wizard's done() method.
def done(self, form_list, **kwargs):
# I don't know whether this will work, I am not familiar with your forms.
for form in form_list:
form.save()
# Note the docs suggest redirecting instead of rendering a template.
return render(self.request, 'Return_to_AMT.html', {
'form_data': [form.cleaned_data for form in form_list],
})
I'm using Django Haystack.
Here is my code:
settings.py
HAYSTACK_CONNECTIONS = {
'default': {
'ENGINE': 'haystack.backends.elasticsearch_backend.ElasticsearchSearchEngine',
'URL': 'http://127.0.0.1:9200/',
'INDEX_NAME': 'haystack',
},
}
HAYSTACK_SIGNAL_PROCESSOR = 'haystack.signals.RealtimeSignalProcessor'
search_indexes.py
class PostIndex(indexes.SearchIndex, indexes.Indexable):
text = indexes.CharField(document=True, use_template=True)
owner = indexes.CharField(model_attr='owner')
image_url = indexes.CharField()
def get_model(self):
return Post
def prepare_image_url(self, obj):
# Get first images for resulted search objects
return [image.image_main_page.url for image in obj.images.order_by('id')[:1]]
def index_queryset(self, using=None):
"""Used when the entire index for model is updated."""
return self.get_model().objects.all()
As you see I use RealtimeSignalProcessor to make it index on Post instance creation or update. And it actually does index the instance on creation except image_url field which is using prepare method. It indexed though on instance update.
Question is why it isn't being indexed on creation?
Any pointers are appreciated.
I ended up with custom signal processor like so:
class RelatedRealtimeSignalProcessor(RealtimeSignalProcessor):
"""
Extension to haystack's RealtimeSignalProcessor not only causing the
search_index to update on saved model, but also for image url, which is needed to show
images on search results
"""
def handle_save(self, sender, instance, **kwargs):
if hasattr(instance, 'reindex_related'):
for related in instance.reindex_related:
related_obj = getattr(instance, related)
self.handle_save(related_obj.__class__, related_obj)
return super(RelatedRealtimeSignalProcessor, self).handle_save(sender, instance, **kwargs)
def handle_delete(self, sender, instance, **kwargs):
if hasattr(instance, 'reindex_related'):
for related in instance.reindex_related:
related_obj = getattr(instance, related)
self.handle_delete(related_obj.__class__, related_obj)
return super(RelatedRealtimeSignalProcessor, self).handle_delete(sender, instance, **kwargs)
And pointed to it in settings:
HAYSTACK_SIGNAL_PROCESSOR = 'your_app.signals.RelatedRealtimeSignalProcessor'