2

I am very new to Django and I was wondering if I could request some help with an issue I am facing. I'm trying to build a set of models in Django that is structured as follows:

An app_user describes a user of the application I am building. An app_job describes a job that the user wants to run on the app, with several associated inputs (upload1, upload2, upload3). A user can run many jobs; hence, I use the many-to-one (ForeignKey) relationship between job and user. When an app_job is created, I want its associated files to be uploaded to a directory determined by the associated user's username and num_jobs attribute, as shown.

When I run python manage.py makemigrations, I receive the following error: AttributeError: 'ForeignKey' object has no attribute 'user'. Which begs the question, how can I access the underlying app_user's information from the app_job class?

Thanks for the help.

# models.py

from django.db import models
from django.contrib.auth.models import User

from django.forms import ModelForm

class app_user(models.Model):
    user = models.OneToOneField(User)
    num_jobs = models.IntegerField(default = 0)

    def __unicode__(self):
        return self.user.get_username()


class app_job(models.Model):
    app_user = models.ForeignKey(app_user)
    upload1 = models.FileField(upload_to = app_user.user.get_username() + "/" + str(app_user.num_jobs) , blank = True, null = True)
    upload2 = models.FileField(upload_to = app_user.user.get_username() + "/" + str(app_user.num_jobs))
    upload3 = models.FileField(upload_to = app_user.user.get_username() + "/" + str(app_user.num_jobs) )
  • 2
    The problem is not in accessing related data, but in trying to access instance attribute data in the class definition. – Daniel Roseman Jul 29 '15 at 19:51
  • 1
    1st: class names should be CamelCase. 2nd: @DMunoz there would be no attribute called user in class `AppJob`. 3rd: `upload_to` can be a callable, but the one you have is an expression, not a callable. 4th point: make sure an `app_user` instance is created when its User is created. It can be achieved [using signals](https://docs.djangoproject.com/en/1.8/topics/signals/#connecting-to-signals-sent-by-specific-senders) (e.g. `post_save` on User) – Pynchia Jul 29 '15 at 19:54

3 Answers3

4

Okay there are a couple of issues here. But nothing a little education cannot fix.

  • models should be CamelCased this makes it easier to read and is generally good practise.
  • You do not need to prefix models with app_ its much cleaner and easier to read without this.

Anyway,

You app_job model ForeignKey should be to User not to the app_user model. By doing this you can still gain access to the app_user data.

You also need to modify the upload_to attributes also. Whilst uploads_to can be a string value it cannot be evaluated the way you are currently doing this. Check out the django filefield documentation for details (shameless plug, I recently re-wrote this part of the documentation).

Instead you need to do the following:

def user_directory_path(instance, filename):
    # file will be uploaded to MEDIA_ROOT/user_<id>/<filename>
    return 'user_{0}/{1}'.format(instance.user.id, filename)

class app_job(models.Model):
    app_user = models.ForeignKey(User)
    upload1 = models.FileField(upload_to=user_directory_path , blank = True, null = True)
    upload2 = models.FileField(upload_to=user_directory_path
    upload3 = models.FileField(upload_to=user_directory_path, blank=True, null=True)

What this is doing is upload_to calls the function user_directory_path to generate the file path.


By following the above you should have something like:

# models.py

from django.db import models
from django.contrib.auth.models import User

from django.forms import ModelForm

class UserProfile(models.Model):
    """
    In settings.py you will want to add a link to AUTH_USER_PROFILE 
    See https://docs.djangoproject.com/en/1.8/topics/auth/customizing/#extending-the-existing-user-model
    """
    user = models.OneToOneField(User)
    num_jobs = models.IntegerField(default=0)

    def __unicode__(self):
        return self.user.get_username()

def upload_dir(instance, filename):
    return instance.user.get_username() + "/" + str(instance.user.num_jobs)

class Job(models.Model):
    user = models.ForeignKey(User)
    upload1 = models.FileField(upload_to=upload_dir, blank = True, null = True)
    upload2 = models.FileField(upload_to=upload_dir, blank=True, null=True)
    upload3 = models.FileField(upload_to=upload_dir, blank=True, null=True)
Matt Seymour
  • 8,880
  • 7
  • 60
  • 101
1

Based on this answer to a similar question something like this should work:

# models.py

from django.db import models
from django.contrib.auth.models import User

class app_user(models.Model):
    user = models.OneToOneField(User)
    num_jobs = models.IntegerField(default = 0)

    def __unicode__(self):
        return self.user.get_username()

def content_file_name(instance, filename):
    return '/'.join([instance.app_user.user.get_username(), str(instance.app_user.num_jobs), filename])

class app_job(models.Model):
    app_user = models.ForeignKey(app_user)
    upload1 = models.FileField(upload_to = content_file_name , blank = True, null = True)
    upload2 = models.FileField(upload_to = content_file_name)
    upload3 = models.FileField(upload_to = content_file_name)    
Community
  • 1
  • 1
DMunoz
  • 367
  • 2
  • 12
0

As you have been told there are several issues. But its good to learn :).

First the class name should start with an upper letter and use "camelCase", in your case, "AppUser" and "AppJob". Personally I would not use "App" as suffix instead I would just use "MyUser" and "Job".

The error you are getting "'ForeignKey' object has no attribute 'user" is because in your "app_job" model you have a ForeignKey to app_user, which is OK, that creates the relationship between the both models, but then in the FileField's you are using that "foreignkey" object which have the same name as your model "app_user" and that ForeignKey instance does not have an attribute called "user", Do you get this?

The information the guys gave you related to the "upload_to" is correct :). you can get more info in the Django docs

Thanks

Community
  • 1
  • 1
Martin Alderete
  • 736
  • 5
  • 9