converting django ForeignKey to a usable directory name - python

I'm working on a django app where the user will be able to upload documents of various kinds. The relevant part of my models.py is this:
class Materials(models.Model):
id = models.AutoField(primary_key=True)
id_presentations = models.ForeignKey(Presentations, db_column='id_Presentations', related_name = "materials_id_presentations") # Field name made lowercase.
materialpathname = 'documents/'
materialpathname += str(id_presentations)
document = models.FileField(db_column='Document', upload_to = materialpathname) # Field name made lowercase.
docname = models.CharField(max_length=40, db_column='DocName') # Field name made lowercase.
class Meta:
db_table = u'Materials'
My intention is for it to save the documents associated with a given presentation, in a subdirectory with the id number for that presentation (so if "Very Important Presentation" is on the database with id 3, it should store the associated materials at the location settings.MEDIA_ROOT/documents/3/whateverdocname.txt ).
However, while the above code "works", it creates a subdirectory that, instead of being named "3", is named <django.db.models.fields.related.ForeignKey object at 0x8e358ec>, or that kind of thing. I've tried using "id_presentations.name", "id_presentations.value", etc. but these attributes don't seem to exist. I can't seem to find a place where it gives a way to get at the integer value of the ForeignKey field, so that I can convert it to a string and use it as a subdirectory name.
Any help is greatly appreciated.

As of Django 1.0, the upload_to argument to FileFields can be a callable. If I'm understanding your intentions correctly, something like this should do the trick:
def material_path(instance, filename):
return 'documents/%d' % instance.id_presentations.id
class Materials(models.Model):
id_presentations = models.ForeignKey(Presentations)
document = models.FileField(upload_to=material_path)
docname = models.CharField(max_length=40)
That model has been simplified a little bit, but hopefully it illustrates the solution. If upload_to is a callable, then every time a file is uploaded Django will call the function, passing it two arguments: the instance to which the file was uploaded is attached and its original filename. You can generate the file path you want by pulling the ID of the presentation off of the instance in question.
More info:
http://docs.djangoproject.com/en/dev/ref/models/fields/#filefield

Provided that the "name" property is defined on your Presentation model, if you're working with a specific instance of the model then what you want should work. Like this:
from models import Materials
obj = Materials.objects.get([some criteria here])
name = obj.id_presentation.name
If you wanted to abstract that to a method on your model, you could do this:
class Materials(models.Model):
def id_presentation_name(self):
return self.id_presentation.name
If you want the database id of the object, you can access either object.id or object.pk.

Note: you can always find out what attributes and methods are available on an object in python by calling dir(object).

Related

How can I add an auto increment field in a model based on total count of objects with same field value?

I'm new to the whole Django thing and a bit lost. Sorry if the title is a bit confusing I'll try to clear things out.
So basically I have two models (Folder and Document). A Document can have a single Folder as one of its fields using a Foreign Key. Now I have another field in Document that needs to get the value of the total Document objects that share the same Folder and increase by one.
I've tried things I read on the docs (aggregation, F() objects, overriding model's save() function) as well as some answers is read here but didn't manage to get it right so I'm posting to get some help. Below is my models.py file with the two models and some comments for better understanding.
class Folder(models.Model):
category = models.IntegerField()
subCategory = models.IntegerField()
name = models.CharField(max_length= 50)
desc = models.TextField()
class Document(models.Model):
folder = models.ForeignKey(Folder, on_delete=models.CASCADE)
date_created = models.DateField()
date_added = models.DateTimeField()
#The field below needs to sum all Document objects that share
#the same folder value in the database + 1 and set it as its default value
f_no = models.IntegerField(default=lambda: Document.objects.aggegate(Count('folder')) + 1)
Thank you in advance, any leads or clues are most welcome
EDIT:
Forgot to say that all management is done via Django's admin dashboard
if this has anything to do at all with my situation. I registered both
models in admin.py and that's all. I make new Folder objects when needed
and save Documents with one specific Folder in them each time
I would recommend creating a ManyToMany relation in the Folder, and add a created Document object into the Folder's ManyToMany relation.
Models.py
class Folder(models.Model):
category = models.IntegerField()
subCategory = models.IntegerField()
name = models.CharField(max_length= 50)
desc = models.TextField()
documents = models.ManyToManyField('app.Document')
You can add can add documents to the folder by using .add() to the ManyToMany relation and the amount of documents in the relation by using .count()
ManyToMany relations are well documented here:
https://docs.djangoproject.com/en/3.1/topics/db/examples/many_to_many/
When you create a Document model object, it represents a single item. That single item shouldn't have a count of how many documents are in a file. The f_no you note should actually be in the Folder model.
Once you create a Document object that is related to a Folder object via ForeignKey, you can use signals to increment the f_no field that resides in the Folder object.
#receiver(post_save, sender=Document)
def increment_folder_item_count(sender, **kwargs):
# get Folder object via Document model instance folder foreignkey field
# folder.f_no += 1
# folder.save()

GAE NDB Confused about Models and duplicating attributes

I'm trying to learn Google App Engine's NDB and I'm confused about the structure of models.
My situation is similar to a CMS platform with Post Types (like in WordPress), so I have "Blogs" and "Pages". All of these Post Types require the same set of attributes: Parent, Name, Slug, Template, Content, Status, and Date.
So far, I gather that I need to create a Model for these like this:
class Post(ndb.Expando):
parent = ndb.StringProperty()
name = ndb.StringProperty()
slug = ndb.StringProperty()
template = ndb.StringProperty()
content = ndb.StringProperty(indexed=False)
status = ndb.StringProperty()
date = ndb.DateTimeProperty(auto_now_add=True)
(I'm using Expando because I will be adding "unknown" attributes in my application)
But with this structure, all of my posts (in every Post Type) will be within the same "kind", so queries will take longer (if I'm not mistaken).
How can I create many Models (kinds) with the same attributes?
Do I copy & paste the above Model under different class names?
Is it possible to create new Models dynamically (similar to "Custom Post Types" in WordPress)? Does it work if I use ndb.Key('Blog', blogid) instead of declaring a Model?
Do I create a Model called class PostType(ndb.Model) that stores the "Post Types" and give them ancestors of Posts? (If I'm not mistaken, this would cause problems because updating a Post would "lock" the entire ancestor tree for a second or so)
My primary goal is efficiency. Thanks!
Updates:
As written by Dan and mgilson, adding sub-classes of the main Post class Model is a good way to solve this:
class Post(ndb.Expando):
parent = ndb.StringProperty()
name = ndb.StringProperty()
slug = ndb.StringProperty()
template = ndb.StringProperty()
content = ndb.StringProperty(indexed=False)
status = ndb.StringProperty()
date = ndb.DateTimeProperty(auto_now_add=True)
class Blog(Post):
pass
However, this requires writing the Models statically. Is there a way to accomplish this dynamically (without declaring them as Models beforehand)?
Update:
Following the advice given below, I decided to keep all of my entities under the same kind. I might decide later on to change this to subclasses (separate kinds for each "Post Type") if my queries get messy. Thank you all for your great advice!
How can I create many Models with the same attributes?
You can subclass:
class SpecialPost(Post):
"""Special post type that is a different kind than Post."""
Though it's often easy enough to use the same kind and just add an extra field that represents the kind of post which you can filter on in queries.
Is it possible to create new Models dynamically (similar to "Custom Post Types" in WordPress)? Does it work if I use ndb.Key('Blog', blogid) instead of declaring a Model?
I'm not 100% sure that I understand what you're asking here. You can dynamically create models the same way you can dynamically create classes in python (using type), but you probably don't want to be doing this. Getting those dynamically created models (and keeping track of their names) will probably end up giving you serious headaches.
Basically a simple example of subclassing, which #mgilson mentioned already.
class Post(ndb.Expando):
parent = ndb.StringProperty()
name = ndb.StringProperty()
slug = ndb.StringProperty()
template = ndb.StringProperty()
content = ndb.StringProperty(indexed=False)
status = ndb.StringProperty()
date = ndb.DateTimeProperty(auto_now_add=True)
class Blog(Post):
someint = ndb.IntegerProperty()
blog = Blog(status='new', someint=2)
key = blog.put()
print key.kind()
As for dynamically creating models, from the Model's Constructor doc:
An application won't normally call Model(), but is likely to call the
constructor of a class that inherits from Model. This creates a new
instance of this model, also known as an entity.
Even if possible (I didn't dig too deep inside ndb/models.py to say with certainty that it's not) it doesn't appear a clear thing. Personally I'd stay away from that and instead re-think the need for such dynamically created models.

Django-models: use field from foreign key

I'm working on an image gallery project in DJango, just for the sake of it. And well, I have a class named Gallery and a class named ImageGallery.
The class named gallery would look like this:
class Gallery(models.Model):
gallery = models.ForeignKey(Gallery, related_name="parent_gallery")
title = models.CharField(max_length=200)
folder = models.CharField(max_length=200) # ex: images/galleries/slugify(self.title)
class ImageGallery(models.Model):
gallery = models.ForeignKey(Gallery, related_name="parent_gallery")
title = models.CharField(max_length=200)
image = models.ImageField(upload_to=self.gallery.folder)
Well, the last line of code is what I want to know if its possible or any other fine replacement for it.
In the DJango-admin I want to be able to add records for the table ImageGallery and when selecting the Gallery I would like to use, the images to be saved to the folder specified in gallery.folder field.
What's the best way to go around this? I haven't completed writing the two classes but I doubt they're gonna work like this. Thank you in advance. :-)
The FileField.upload_to is defined as follows
This attribute provides a way of setting the upload directory and file
name, and can be set in two ways. In both cases, the value is passed
to the Storage.save() method. ... upload_to may also be a callable,
such as a function. This will be called to obtain the upload path,
including the filename. This callable must accept two arguments and
return a Unix-style path (with forward slashes) to be passed along to
the storage system. The two arguments are:
But self.gallery.folder isn't a callable. What you need is to setup a function along the lines given in that example
def get_upload_path(instance, filename):
return '{0}/{1}'.format(instance.gallery.folder, filename)
And your model will change to
image = models.ImageField(upload_to=get_upload_path)

Django get_or_create does not return a usable Model object in clean method of ModelForm

Hello,
I have bound a ModelForm to one of my model that contains a ForeignKey to another model everything driven by a CreateView. What I want to achieve is to create the model object corresponding to the foreign key if it doesn't exist before the form is overall validated and the final object created in database.
Below the models I use:
class UmsAlerting(models.Model):
alert_id = models.IntegerField(primary_key=True, editable=False)
appli = models.ForeignKey('UmsApplication')
env = models.ForeignKey('UmsEnvironment')
contact = models.ForeignKey('UmsContacts')
custom_rule = models.ForeignKey('UmsCustomRules', null=True, blank=True)
class UmsApplication(models.Model):
appli_id = models.IntegerField(primary_key=True)
trigram_ums = models.CharField(max_length=4L)
class UmsContacts(models.Model):
contact_id = models.IntegerField(primary_key=True)
mail_addr = models.CharField(max_length=100L)
class UmsEnvironment(models.Model):
env_id = models.IntegerField(primary_key=True)
env_name = models.CharField(max_length=5L)
The model bound to the form is UmsAlerting. The model object I want to create if it doesn't exist is UmsContacts. I managed to use the field's clean method in my ModelForm of the contact field and use the get_or_create method like below:
def clean_contact(self):
data = self.cleaned_data['contact']
c, _ = UmsContacts.objects.get_or_create(mail_addr=data)
return c
It perfectly works when the contact is already in the database but when it needs to be created my form return a ValidationError on the contact field saying "This field cannot be null". If I submit the same form a second time without changing anything the UmsAlerting object is well created with no validation error.
My guess is that, for a reason I don't get, when get_or_create is used to create a UmsContacts object it cannot be used to create the new UmsAlerting object. So in clean_contact method the get is working and returns the UmsContacts object but the create part doesn't. It'd be like the UmsContacts object is saved when the whole form is validated but not before as I'd want it to.
Anyone could help me find out what is the problem ? Is using the clean method not the best idea ? Is there another strategy to use to take around this problem ?
Thanks in advance for your help.
It's probably because the object you are creating expects value for contact_id. If you use contact_id field for just setting object id -then you do not have to create it at all. Django takes care of Id's automatically.
Also. field clean method should return cleaned data not object. That creates whole lot more problems on its own.

Django Mongodb ListField not saving or updating

I am starting to create a webapp using Django and MongoDB. Everything is working fine when I create a model and save it into the Database. Now, I do a "Class.objects.get()" to get the object I need from my DB and I have one field called "media" which is a ListField(). I had tried doing either:
Concert.media.append(list)
or
Concert.media.extend(list)
and then
Concert.save()
This is my "Concert" object in my models.py:
class Concert(models.Model):
main_artist = models.CharField(max_length=50)
concert_id = models.AutoField(primary_key=True)
openers = ListField(EmbeddedModelField('Opener'))
concert_date = models.DateField()
slug = models.SlugField(unique=True)
media = ListField()
And when I go to see the results in does not update the object. No values where saved. If someone can help me I going to give a super cyber fist bump.
Concert is a class, not an instance. You can't save a class. You need to make an instance of the class and save that. Something like
c = Concert()
c.media.append(list)
c.save()
(btw, just as a note, list is a bad variable name because list is a type in python. Never use types as variable names (though everyone is guilty of this at one point or another, including me.))

Categories