Question

I have a problem with the classes design i'm doing.

What i need is to be able to get the last media files that were uploaded through the admin. The media files could be songs, albums, and videos.

So i have the next classes:

class MediaFile(models.Model):
    name = models.CharField(max_length=50)
    created_at = models.DateTimeField(auto_now_add=True)
    last_update = models.DateTimeField(auto_now=True)

class Song(models.Model):
    media_data = models.OneToOneField(to=MediaFile) # relation with MediaFile
    file = models.FileField(upload_to='mp3')
    album = models.ManyToManyField(Album)

class Album(models.Model):
    media_data = models.OneToOneField(to=MediaFile) # relation with MediaFile
    cover = models.ImageField(upload_to='albums')
    release_date = models.DateField()

class Video(models.Model):
    media_data = models.OneToOneField(to=MediaFile) # relation with MediaFile
    summary = models.TextField()

It is validated ok, but when i run the project, it returns me the next error:

<class 'MediaFile'> has no ForeignKey to <class 'Song'>

So, do you know what i am doing wrong? Do you know if there is a better way to achieve this? I mean, what i have to achieve is be able to get the last 10 media files uploaded without have to get first rows of songs, then of albums, and then of videos.

I accept critics and suggestions, thanks!

EDIT:

The error happen here: environment\lib\site-packages\django\forms\models.py in _get_foreign_key, line 942

EDIT 2:

Here is the Traceback

> Traceback Switch to copy-and-paste view

\environment\lib\site-packages\django\core\handlers\base.py in get_response
                    response = wrapped_callback(request, *callback_args, **callback_kwargs) ...
▶ Local vars
\environment\lib\site-packages\django\contrib\admin\options.py in wrapper
                return self.admin_site.admin_view(view)(*args, **kwargs) ...
▶ Local vars
\environment\lib\site-packages\django\utils\decorators.py in _wrapped_view
                    response = view_func(request, *args, **kwargs) ...
▶ Local vars
\environment\lib\site-packages\django\views\decorators\cache.py in _wrapped_view_func
        response = view_func(request, *args, **kwargs) ...
▶ Local vars
\environment\lib\site-packages\django\contrib\admin\sites.py in inner
            return view(request, *args, **kwargs) ...
▶ Local vars
\environment\lib\site-packages\django\utils\decorators.py in _wrapper
            return bound_func(*args, **kwargs) ...
▶ Local vars
\environment\lib\site-packages\django\utils\decorators.py in _wrapped_view
                    response = view_func(request, *args, **kwargs) ...
▶ Local vars
\environment\lib\site-packages\django\utils\decorators.py in bound_func
                return func(self, *args2, **kwargs2) ...
▶ Local vars
\environment\lib\site-packages\django\db\transaction.py in inner
                return func(*args, **kwargs) ...
▶ Local vars
\environment\lib\site-packages\django\contrib\admin\options.py in add_view
            for FormSet, inline in zip(self.get_formsets(request), inline_instances): ...
▶ Local vars
\environment\lib\site-packages\django\contrib\admin\options.py in get_formsets
            yield inline.get_formset(request, obj) ...
▶ Local vars
\environment\lib\site-packages\django\contrib\admin\options.py in get_formset
            fields = flatten_fieldsets(self.get_fieldsets(request, obj)) ...
▶ Local vars
\environment\lib\site-packages\django\contrib\admin\options.py in get_fieldsets
        form = self.get_formset(request, obj, fields=None).form ...
▶ Local vars
\environment\lib\site-packages\django\contrib\admin\options.py in get_formset
        return inlineformset_factory(self.parent_model, self.model, **defaults) ...
▶ Local vars
\environment\lib\site-packages\django\forms\models.py in inlineformset_factory
    fk = _get_foreign_key(parent_model, model, fk_name=fk_name) ...
▶ Local vars
\environment\lib\site-packages\django\forms\models.py in _get_foreign_key
            raise Exception("%s has no ForeignKey to %s" % (model, parent_model)) ...
▶ Local vars

EDIT 3:

Here i add the admin.py

class SongInline(admin.TabularInline):
    model = Song

class AlbumAdmin(admin.ModelAdmin):
    inlines = [
        SongInline,
    ]


class MediaFileInline(admin.TabularInline):
    model = MediaFile

class MediaFileAdmin(admin.ModelAdmin):
    inlines = [
        MediaFileInline,
    ]

admin.site.register(MediaFile)
admin.site.register(Song, MediaFileAdmin)
admin.site.register(Video)
admin.site.register(Album, AlbumAdmin)
Was it helpful?

Solution

Your inline has to be defined for the model that has the OneToOneField defined on it. Below is something you could do.

class SongInline(admin.TabularInline):
    model = Song

class AlbumInline(admin.TabularInline):
    model = Album

class VideoInline(admin.TabularInline):
    model = Video

class MediaFileAdmin(admin.ModelAdmin):
    inlines = [
        SongInline,
        AlbumInline,
        VideoInline
    ]

admin.site.register(MediaFile, MediaFileAdmin)
admin.site.register(Song)
admin.site.register(Video)
admin.site.register(Album)

OTHER TIPS

I believe what's happening is that Django wants a primary key(PK) for integrity reasons with your OneToOneField. Since your model has an implied PK (the auto increment integer kind by default), it's trying to link two things together but hits a wall when it tries to get the objects. In your models try the following:

class Song(models.Model):
    media_data = models.OneToOneField(MediaFile, primary_key = True)
    file = models.FileField(upload_to='mp3')
    album = models.ManyToManyField(Album)

For more information try: Django's OneToOne Documentation

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top