How to resolve name error on variable in class - python

I am making my first model, and I'm creating an upload system which uploads to a folder with the name of the user uploading it.
For some reason, I get this error when I try to create an object from the model:
NameError at /admin/tracks/track/add/
name '_Track__user_name' is not defined
Here's my models.py
from django.core.exceptions import ValidationError
from django.db import models
from django.core.files.images import get_image_dimensions
# Create your models here.
class Track(models.Model):
user_name = "no_user"
def get_username():
user_name = "no_user"
if request.user.is_authenticated():
user_name = request.user.username
else:
user_name = "DELETE"
def generate_user_folder_tracks(instance, filename):
return "uploads/users/%s/tracks/%s" % (user_name, filename)
def is_mp3(value):
if not value.name.endswith('.mp3'):
raise ValidationError(u'You may only upload mp3 files for tracks!')
def generate_user_folder_art(instance, filename):
return "uploads/users/%s/art/%s" % (user_name, filename)
def is_square_png(self):
if not self.name.endswith('.png'):
raise ValidationError("You may only upload png files for album art!")
else:
w, h = get_image_dimensions(self)
if not h == w:
raise ValidationError("This picture is not square! Your picture must be equally wide as its height.")
else:
if not (h + w) >= 1000:
raise ValidationError("This picture is too small! The minimum dimensions are 500 by 500 pixels.")
return self
# Variables
track_type_choices = [
('ORG', 'Original'),
('RMX', 'Remix'),
('CLB', 'Collab'),
('LIV', 'Live'),
]
# Model Fields
name = models.CharField(max_length=100)
desc = models.TextField(max_length=7500)
track_type = models.CharField(max_length=3,
choices=track_type_choices,
default='ORG')
track_type_content = models.CharField(max_length=100,blank=True)
created = models.TimeField(auto_now=True,auto_now_add=False)
upload = models.FileField(upload_to=generate_user_folder_tracks,validators=[is_mp3])
albumart = models.ImageField(upload_to=generate_user_folder_art,validators=[is_square_png])
As you can see from the first line after the class is defined, there is clearly a variable called "user_name", and when using my upload functions, it is supposed to use this variable for the folder name.
I am very confused to why this is throwing an error, what am I doing wrong?

You have some serious problems with variable scope here. Just defining an attribute called "user_name" at the top of the class does not automatically give you access to it elsewhere in the class; you would need to access it via the class itself. Usually you do that through the self variable that is the first parameter to every method.
However, many of your methods do not even accept a self parameter, so they would give TypeError when they are called. On top of that, your user_name attribute is actually a class attribute, which would be shared by all instances of User - this would clearly be a bad thing. You should really make it a Django field, like the other attributes.
Finally, your scope issues worsen when you try and access request in one of those methods. Again, you can't access a variable unless it has been passed to that method (or is available in global scope, which the request is definitely not). So get_username cannot work at all.
I must say though that all that is irrelevant, as the error you get does not even match your code: you must have accessed Track.__user_name somewhere to get that error.

You do have a variable username, but its not a field which would mean that the query set it looks like you're creating won't find it
user_name = "no_user"
should be one of the following
user_name = models.CharField(default='no_user')
user = models.ForeignKey(settings.AUTH_USER_MODEL, null=True)
The only reason I've suggested a CharField here is incase you don't use some form of authorisation user model in your app. If you do, then you should use a foreign key to that model.

Related

Limit ForeignKey models by count

This is my model and I want to limit the number of photos that a user can upload just 10. I want to do it one place so it works in the admin and user facing forms. Can someone help me out here?
class StarPhotos(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
PHOTO_CATEGORY = (
('HS', "Head Shot"),
('WP', "Western Party Wear"),
('IP', "Indian Party Wear"),
('SW', "Swim Wear"),
('CW', "Casual Wear"),
)
category = models.CharField(max_length=2, choices=PHOTO_CATEGORY, default='CW')
# This FileField should preferaby be changed to ImageField with pillow installed.
photos = models.FileField(max_length=200, upload_to='images/',)
def __str__(self):
return "Images for {0}".format(self.user)
You can use a checker function in your model to check whether User has uploaded 10 photos or not
def check_photo_count(self, user):
photo_count = self.object.filter(user=user).count()
return photo_count
This does not seem to be the best solution available but it should work.
Also remember to put a check for this in your views or admin. You can also return a Boolean value from this function saying that this user is allowed to upload more photos or not.
This same logic can be applied to manager if you don't want to put checks everywhere.
So you just have to put this check in the create method and if the check fails simply raise a error or return a false value saying that object is not created.
You can override save and bulk_create methods from StarPhotos, I don't check the code, but it's some like that:
class CheckPhotoModelManager(models.Manager):
def bulk_create(self, objs, batch_size=None):
photos = StarPhotos.object.filter(user=objs[0].user).count()
if photos < 10:
super(StarPhotos, self).bulk_create(...)
class StarPhotos(models.Model):
objects = CheckPhotoModelManager()
def save(self, *args, **kwargs):
photos = StarPhotos.object.filter(user=self.user).count()
if photos < 10:
super(StarPhotos, self).save(*args, **kwargs)

how to modify related object's data whilie creating a new object in django

I am new to django and I'm trying to do something pretty simple.
my models.py is as below:
from django.db import models
class DiskDrive(models.Model):
deviceId = models.CharField(max_length=64, primary_key=True)
freeSpace = models.BigIntegerField()
def __unicode__(self):
return self.deviceId
class StoragePool(models.Model):
poolId = models.CharField(max_length=256, primary_key=True)
size = models.BigIntegerField()
drive = models.ForeignKey(DiskDrive, related_name='pools')
def __unicode__(self):
return self.poolId
I haven't added anything to views.py and urls.py yet.
I'm able to create objects of both the classes.
Whenever I create an object of StoragePool class, I want to reduce the value of 'freeSpace' attribute of the related DiskDrive object by the 'size' of 'StoragePool' object. How should I do this? Please help...
Sounds like the perfect job for a post_save signal:
#receiver(post_save, sender=StoragePool)
def update_drive_space(sender, instance, created, **kwargs):
if created:
instance.drive.freeSpace = F('freeSpace') - instance.size
instance.drive.save(update_fields=['freeSpace'])
See Django signals documentation for more info about how signals work. In a nutshell this method will be called each time you create or update a StoragePool object.
Few notes:
I am using F expression to reference current database size value instead of blindly saving whatever we have on Django side (this will ensure correct value when multiple clients will create new StoragePool objects)
It is likely that you want to adjust freeSpace attribute also when size is updated - not just on StoragePool creation. To make that happen just delete if created check and it will be run on every StoragePool.save()

Cannot change model instance after a ValidationError is thrown in django admin area

Imagine a model like this:
class CFile(models.Model):
filepath = models.FileField(upload_to=...)
collection = models.ForeignKey("FileCollection",null=True)
... # other attributes that are not relevant
def clean(self):
bname = os.path.basename
if self.collection:
cfiles = self.baseline.attachment_set.all()
with_same_basename = filter(lambda e: bname(e.filepath.path) == bname(self.filepath.path),cfiles)
if len(with_same_basename) > 0:
raise ValidationError("There already exists a file with the same name in this collection")
class FileCollection(models.Model):
name = models.CharField(max_length=255)
files= models.ManyToManyField("CFile")
I want to disallow the upload of a CFile if there already exists a CFile with the same basename, that's why I added the clean. The problem is:
I upload a CFile, with the name file1.png -> gets uploaded because no other files with this name exist
I upload another CFile, with the name file1.png -> I get the expected error that I already have a file with this name. So, I try to change the file, and upload a file with a different name ( file2.png ). The problem is, I stopped via pdb in the clean, and the model instance is still file1.png. I imagine this happens because of my ValidationError and django allows me to "correct my mistake". The problem is I cannot correct it if I cannot upload another file. How can I handle this?
EDIT: This happens in the admin area, sorry for forgetting to mention this before. I don't have anything custom ( besides inlines = [ FileInline ] ).
I think the clearest way is to declare another field in your model for filename and make it unique for every collection. Like this:
class CFile(models.Model):
filepath = models.FileField(upload_to=...)
collection = models.ForeignKey("FileCollection",null=True, related_name='files')
filename = models.CharField(max_length=255)
... # other attributes that are not relevant
class Meta:
unique_together = (('filename', 'collection'),)
def save(self, *args, **kwargs):
self.filename = bname(self.filepath.path)
super(CFile, self).save(args, kwargs)

Accessing Django custom models through Manager returns empty set

From all I've read, it appears this should Just Work, but it doesn't.
I have a custom model:
from django.db import models
from django.contrib.auth.models import *
class Feed(models.Model):
user = models.ForeignKey(User, blank=True, null=True)
link = models.CharField(max_length=200)
startDate = models.CharField(max_length=8)
endDate = models.CharField(max_length=8)
def __unicode__(self):
return str(self.id)
def __init__(self, link, sDate, eDate, user=None):
super(Feed, self).__init__()
self.link = link
self.startDate = sDate
self.endDate = eDate
self.user = user
And I'm also using the User model included in 'django.contrib.auth.models'.
When I create a feed, e.g.
feed = Feed(link, sDate, eDate)
feed.save()
(or a similar one with a user specified) it appears to store it in the database (I get its PK which keeps incrementing), but 'Feed.objects.all()' returns an empty list. Trying to filter by an existing pk also returns an empty list and trying to get() an existing pk gives me the following error
TypeError: __init__() takes at most 5 arguments (6 given)
Looking at how I should be retrieving objects from custom models, it appears that I've done everything I should, but that is clearly not the case...
Whoa.
Why are you overriding your model's __init__? There are very few good reasons to do this, and if you do, you must absolutely keep the interface the same- because that __init__ is called every time django pulls one of your models from the db (which is why you get the error when you call .get())
What are you hoping to accomplish with your __init__?
You should probably just delete your __init__ and then you can create Feed objects the normal, django way:
feed = Feed(link=link, startDate=sDate, endDate=eDate)
That line will create the correct feed object you want.
Did you try named arguments, e.g.
feed = Feed(link=link, startDate=sDate, endDate=eDate)
How did you use get()? It should also be used with named arguments, e.g.:
Feed.objects.get(pk=6)

ForeignKey model has no Manager (i.e., 'Foo' object has no attribute 'foo_set')

I have searched around for an answer to this but can't find one. When using a ForeignKey, I am consistently getting an error telling me that 'Foo object has no attribute 'foo_set'. I am a bit new to Django/Python, so I'm sure there is a simple answer here, but I haven't been able to find it so far. Here's some code (to store varied Boards for use in a game, each of which should have a number of Hexes associated with it):
Models:
class Boards(models.Model):
boardnum = models.IntegerField(unique=True)
boardsize = models.IntegerField(default=11)
hexside = models.IntegerField(default=25)
datecreated = models.DateTimeField(auto_now_add = True)
class Hexes(models.Model):
boardnum = models.ForeignKey(Boards, null = True)
col = models.IntegerField()
row = models.IntegerField()
cost = models.IntegerField(default=1)
Code (this works):
newboard, createb = Boards.objects.get_or_create(boardnum=boardn)
createb returns True.
Code (this immediately follows the above, and does not work):
try:
hx = newboard.boards_set.create(col=c, row=r)
except Exception, err:
print "error:", err
traceback.print_exc()
Both "err" and "traceback.print_exc()" give: AttributeError: 'Boards' object has no attribute 'boards_set'
I get the same error if I first create the Hexes record with a get_or_create and then try a newboard.boards_set.add() on it.
Any ideas? All suggestions appreciated.
The name that Django uses for a reverse foreign key manager is the name of the model that contains the foreign key, not the name of the model that the manager is on.
In your case, it will be:
newboard.hexes_set.create(col=c,row=r)
I find it useful to use the manage.py shell command to import your models and inspect them (with dir, etc) to check out all the available attributes.

Categories

Resources