I'm making a little piece of software with Django and JS that will handle image uploads. So far, so good. I'm getting nice little images via AJAX from dropzone.js. They are saved on the file system and have an ImageField in my Photo model to keep track of what is stored and where.
I even stabbed dropzone.js to nicely ask my dev server to delete the database entries and the files themselves. I find that the latter part is lacking a bit. So I started writing a function that catches a post_delete signal from my Photo model and has the task of handling the deletion from the file system. The problem is, I can't seem to find a way to get my hands on the file path that's stored in database.
If I've understood correctly, the following should work:
from django.db import models
from django.db.models.signals import post_delete
from django.dispatch import receiver
class Photo(models.Model):
imageFile = models.ImageField(upload_to=generateImageFileNameAndPath)
#receiver(post_delete, sender=Photo)
def cleanupImageFiles(sender, **kwargs):
print("Cleanup called")
p = kwargs['instance']
path = p.imageFile.name
print(path)
But when I try to output path to the console, there's nothing.
Sorry about the upperCasing instead of using under_scores as seems to be Python convention. I personally find the underscore convention a bit annoying and am having a wrestling match inside my head over whether to follow the convention or just go my own way. For now, I've done the latter.
edit: I can't seem to make it work with p.imageFile.url either as suggested here.
editedit: I also tried with pre_delete signal thinking that maybe post_delete the data has already been blown to smithereens, which would be dumb, but who knows :)
edit3: calling imageFile.path, doesn't cut it either. It just produces
[27/Nov/2016 22:29:08] "POST /correcturl/upload/ HTTP/1.1" 200
Cleanup called
[27/Nov/2016 22:29:15] "DELETE /correcturl/upload/ HTTP/1.1" 500 37
on the console window. The HTTP error 500 just comes from the view not being able to handle the delete call because of this code not working properly. That's what I use as status message to the frontend at this point.
It might be worth noting, that if I do
print(p)
the output on the console is
Photo object
If you need the path of the image, try:
path = p.imageField.path
P.S.: Yes, you should follow the convention. Otherwise it will be hard for others to read your code if you share it with somebody, or contribute to an open source project, or hire programmers in your company, etc.
I knew I had to have done some stupid and finally had time to get back to debugging.
In my view I'd done
deletable = Photo(id=id)
instead of
deletable = Photo.objects.get(id=id)
thus ending up with a new photo object with just the id field filled in. Because Photo.save() was never called this didn't end up in my DB and no errors were thrown. Because of this, the bug flew stealthily under my radar.
Thus, when finally calling
deletable.delete()
it only removed the uncomplete instance I had just created. Although, it also deleted the proper entry from the DB. This is what threw me off and made me mostly look elsewhere for the problem thinking I had the correct database object in my hands.
Where this behavior came from remains unclear to me. Does delete() actually check the database for the id (which it in this case would've found) instead of just handling the instance in question? I guess taking a look at django.db.models.Model.delete() could shed some light on this.
Related
I have been learning python for some months, and started tinkering with Django. Before posting this, I read up on the auto-generated 'admin.py'-code on github, as well as googled the matter. It appears my question is a little specific, and I was quite frankly very confused from that specific reading. Thus, I hope asking this adds value to this wonderful community. Question:
When connecting a model to the admin page, in admin.py, you first import admin:
from django.contrib import admin
After this, you import your model. Then, you supposedly connect your model through:
admin.site.register(MODEL)
What I do not understand is what 'site.register' is. The fact that the line starts off with 'admin.' makes perfect sense, as you are specifying from where the following import (ex. 'admin.function' or 'admin.class') comes from. Had it only been 'admin.somefunctionfromadmin' I would have totally understood this. Now, instead, I am confused as to what 'sites.register' is.
Is 'sites' a module, a file, and 'register' a function from within that module? If so, what does that make 'admin'? A package?
I have seen lines similar to these throughout Django, and feel a bit confused.
Thank you!
admin.site is the default instance of the AdminSite class. It is instantiated in django.contrib.admin.sites.
It is then imported in the django/contrib/admin/__init__.py. This makes it available as admin.site when you have done from django.contrib import admin.
When you call admin.site.register(Model), you are calling the register method of this admin site instance.
In the clean method of my form class I am working with many different inputs from the billing, contact, and account sections of the form. As such there are many self.add_error statements and many fields that depend on other fields in order to validate.
I have noticed that after adding an error for a field I am unable to access that field any more. This is strange as you can add more than one error to a field, but that is not the issue.
I am seeing this method grow more complicated and unreadable, is there a good way to do this so the person that comes after me will understand it? I don't feel that the code ordering to prevent access after error is appropriate. My only thought is to set error variables in clean and call a different method at the end to add the errors to the fields.
Thanks
Edit: To add, I am only returning after clean has run in order to give the user all errors at once. I did not feel that returning after each found error was a good user experience
It's good practice to provide your code to help people see what you are doing. This includes your error reports/traceback info. Use the markdown options to help make your post more readable.
I could be wrong but from my own experience, I usually clean each field individually as per the documentation unless they rely on each other such as passwords, etc.
you can read this part of the documentation to help get some clarity:
https://docs.djangoproject.com/en/1.10/ref/forms/validation/#cleaning-a-specific-field-attribute
I'm going through and checking the various pages in our project, and the great majority appear to be working fine after the upgrade. However, when I try to view a particular entry in the admin, I get this error. On viewing the stack trace, everything is being done internally within Django's admin code, so there is no place in my own code that I can go to debug. This would suggest either that there is something wrong with the Django admin or that there is some release note I missed that is necessary to make this work properly. Any ideas? The actual error is happening here:
site-packages/django/contrib/admin/helpers.py in contents, line 206
if isinstance(f.rel, ManyToManyRel) and value is not None:
result_repr = ", ".join(map(six.text_type, value.all()))
else:
result_repr = display_for_field(value, f)
Obviously, I could go into Django and hack around, but I shouldn't have to do this on a new installation. Any help on pinpointing the issue would be much appreciated. I'm just staring t the screen at this point.
Was responding to Alasdair's comment and got to playing with our admin code, and I was able to narrow it down to the method call that was causing the error.
We have a Lead model that relates to our Company model via a ManyToManyField (i.e. one lead can be for one or more companies). The field that relates Lead to Company has a related_name of "leads".
class Company(models.Model):
...
class Lead(models.Model):
companies = models.ManyToManyField(Company, blank=True, related_name='leads')
...
The CompanyAdmin, looks like the following:
class CompanyAdmin(admin.ModelAdmin):
...
readonly_fields = 'leads',
...
def leads(self, obj):
...
So what appears to have been happening was, when we were trying to access the leads method from CompanyAdmin, Django was instead trying to access the company's Lead objects via the related name -- the ManyToManyField that was throwing the error. I resolved the conflict by changing the method name in the admin to "my_leads".
Looks like something was changed somewhere between 1.8 and the final version of 1.6 that has opened the door to potential conflict between related names and admin methods. The solution, make sure there is no overlap in naming, and things should work fine.
I'm new to both Django and Python so please forgive me if I come off as annoying....I'm just very much misinformed!
Error Code: http://i.gyazo.com/68d88cabf536b129dc37cde6c3ae319c.png
I've googled about this 'KeyError' and it seems to be related to clean(). However, the example my lecturer gave me worked ok without it but when I tried to recreate what he gave me I kept getting this error.
A bit of info: I had originally had a ForeignKey for the user for each submission so I changed it to a simple form to fill in (not a permanent solution) but I still get a KeyError.
Here is my models, views and forms:
http://pastebin.com/rAX5PDHQ
Sorry if I left something out. I'll respond ASAP if you all need more info.
Again, sorry if this is a silly question. But I'm utterly lost to be honest.
Thank you!
PS Sorry I really tried the code formatting but I kept getting an error saying it was incorrect thought the preview said it was ok. And I can't post more than one link.
You don't have a field named user in your form. Try changing the relevant line to:
bd = BloodData (respondent=cd['respondent'],
in your "storeBloodData" view.
The problem seems to be in your view storeBloodData,at this point.
bd = BloodData (respondent=cd['user'],
The form has no field named 'user'.You may replace it with a relevant field present declared in the form.
Also, it is better to use DICT.get(key)when you are not sure if the dict contains that particular key or not. This way you'll simply be returned None when the key is absent and you'll be able to dodge KeyError.
I have created a function log_error(request, traceback), which I call in my exceptions. This writes error information to the database. Now, before I open up every one of my views and add this in an exception handler, is there a way to automatically have all exceptions raise to a function, which then calls this?
I have seen this Python error logging, which says to write your own version of sys.excepthook. This function is automatically called when there is an exception. I tried this, but my_excepthook was not called even though I copy-pasted the solution into views.py and raised an error. However, I didn't try too hard because it's not getting all the information that I need, anyway. I also need request so I can log information abut the user, url, etc.
Maybe that's asking too much?
(I'm using Django, but this does not seem like a Django-specific thing) Edit: yes, it is.
J.F Sebastian's suggestion worked. This is a Django solution.
In settings.py MIDDLEWARE_CLASSES:
(I added it as the last one, not sure if this is right or will cause errors down the line. Works for now.)
'myapp.middleware.ExceptionMiddleware',
In myapp.middleware.py:
import traceback
class ExceptionMiddleware(object):
def process_exception(self, request, exception):
log_error(traceback, request)
That's it. log_error is my function and writes to the database. It also appears from the documentation https://docs.djangoproject.com/en/dev/howto/error-reporting/ that I can get the local variables as well as the request attributes.