Modifying django object model for better admin CRUD management - django

I am writing a blog app using django 1.10
This is a snippet of my object model:
model.py
class Attachment(models.Model):
title = models.CharField(max_length=50)
image = models.ImageField(upload_to='attachments')
class FileAttachments(models.Model):
title = models.CharField(max_length=50)
attachments = models.ManyToManyField(Attachment)
class Post(models.Model):
title = models.CharField(max_length=200)
text = models.CharField(max_length=2000)
file_attachments = models.ForeignKey(FileAttachments, blank=True, null=True)
slug = models.SlugField(max_length=40, default='', unique=True)
author = models.ForeignKey(User, default=1)
pub_date = models.DateTimeField(blank=True, null=True)
def get_absolute_url(self):
return "/blog/%s/%s/%s/%s/" % (self.pub_date.year, self.pub_date.month, self.pub_date.day, self.slug)
def __unicode__(self):
return self.title
class Meta:
verbose_name = "Blog Post"
verbose_name_plural = "Blog Posts"
ordering = ["-create_date"]
permissions = (
( "create", "Create Post" ),
( "modify", "Modify Post" ),
( "delete", "Delete Post" ),
)
(simplified) admin.py:
class PostAdmin(admin.ModelAdmin):
prepopulated_fields = {"slug": ("title",)}
exclude = ('author',)
def save_model(self, request, obj, form, change):
obj.author = request.user
obj.save()
# Register your models here.
admin.site.register(Post, PostAdmin)
when I try to access the Post object via the admin page - in the list view, I only see 'Post object' - whereas I want to see the title of the post (and possibly, a few other attributes of the Post object) - how do I modify the admin view to achieve this?

For your first problem - you need to define list_display in your PostAdmin, like that:
class PostAdmin(admin.ModelAdmin):
prepopulated_fields = {"slug": ("title",)}
exclude = ('author',)
list_display = ('title', 'pub_date')
As for your second problem - please stick to the 'One post, one problem' rule; hint - ForeignKey means only one FileAttachment can be related to your Post.

Change __unicode__ for __str__ in your Post class and print whatever you want. For example: return "Blog %s published on %s" %(self.title, self.pub_date).
Your Post model only includes one attachment through a foreign key. That way it will be impossible to upload more than one file. In other words, you have to change your models, for example including a key in FileAttachments relating to a Post and removing the key from Post model.
Hope this helps.

Related

How to filter data dynamically by supply values from form using django

I want to filter Blog Post objects or records based on the Post Category and a User that uploaded the Post record, it gives me an error when I try to do filter, this is the error.
ValueError at /dashboard/filter-post/
The QuerySet value for an exact lookup must be limited to one result using slicing.
Here is my models.py
class Category(models.Model):
cat_name = models.CharField(max_length=100, verbose_name='Category Name')
cat_desc = models.TextField(blank=True, null=True)
def __str__(self):
return self.cat_name
class Meta():
verbose_name_plural='Category'
class Post(models.Model):
pst_title = models.CharField(max_length=150)
pst_image = models.ImageField(blank=True, null=True, upload_to='uploads/')
user = models.ForeignKey(User, on_delete=models.CASCADE)
category = models.ManyToManyField(Category)
content = models.TextField()
created = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.pst_title
#property
def img_url(self):
if self.pst_image:
return self.pst_image.url
on forms.py
class FilterForm(forms.ModelForm):
user = forms.ModelChoiceField(
queryset=User.objects.all(),
widget=forms.Select(attrs={'class': 'form-control'}))
category = forms.ModelMultipleChoiceField(
queryset=Category.objects.all(),
widget=forms.SelectMultiple(attrs={'class': 'form-control js-example-disabled-results'}))
catch_bot = forms.CharField(required=False,
widget=forms.HiddenInput, validators=[validators.MaxLengthValidator(0)])
class Meta():
fields = ['user', 'category' ]
model = Post
on views.py
def filter_post(request):
post = FilterForm(request.GET)
queryset = Post.objects.all()
if post.is_valid():
user=post.cleaned_data.get('user')
category=post.cleaned_data.get('category')
if user and category:
queryset = queryset.filter(user__username=user, category__cat_name=category)
return render(request, 'backend/filter-post.html', {'query':queryset, 'post':post})
I am having challenges properly filtering this in my views any help?
Try this:
instead of this:
queryset = queryset.filter(user__username=user, category__cat_name=category)
use this:
queryset = queryset.filter(user=user, category=category)
Also don't name your model fields after the model name, just use name instead of pst_name or cat_name, you will see that when you will try access these values there will be no confusion.
UPDATE
Ok, maybe try to rewrite your view like this:
def filter_post(request):
posts = Post.objects.all()
form = FilterForm(request.GET) # its best practice to call your form instance `form` in the view so that the next line has better readability
if form.is_valid():
user=post.cleaned_data['user']
category=post.cleaned_data['category']
if user:
posts = posts.filter(user=user)
if category:
posts = posts.filter(category=category)
return render(request, 'backend/filter-post.html', {'posts':posts})

Try to join a OneToOne relationship in Django

I need some help doing a join using Django, which seems like it should be easy. I have looked at the documentation but it seems like it won't join for some reason.
I am trying to get in my view, the model.Photo and model.PhotoExtended with both joined and then displayed in the view. Currently I am just trying to get the model.Photo displayed but with a join which finds the request.user and filters it based on that.
They are in different apps.
models.py for model.Photo
class Photo(ImageModel):
title = models.CharField(_('title'),
max_length=60,
unique=True)
slug = models.SlugField(_('slug'),
unique=True,
help_text=_('A "slug" is a unique URL-friendly title for an object.'))
models.py for model.PhotoExtended
class PhotoExtended(models.Model):
Photo = models.OneToOneField(Photo, related_name='extended', help_text='Photo required', null=True)
user = models.ForeignKey(User, on_delete=models.CASCADE, null=True, help_text='User that uploaded the photo')
views.py
class PhotoExtendedUserView(ListView):
template_name = 'photo_user_list.html'
def get_queryset(self):
user = get_object_or_404(User, username=self.request.user)
return Photo.objects.filter(photoextended__user=user)
You set the related_name on Photo (which shouldn't be capitalized by the way) to extended so you need to filter like so:
class PhotoExtendedUserView(ListView):
template_name = 'photo_user_list.html'
def get_queryset(self):
user = get_object_or_404(User, username=self.request.user)
# 'extended' vs. 'photoextended'
return Photo.objects.filter(extended__user=user)

Show post in django admin page but don't on site

I'm creating simple blog and face with problem. I need to have separate type of posts in Django admin page which would be saved, but not showed on site. For example, when somebody suggest post, I want at first read it and after that publish, or when I'm writing a post and want go on after some time I need to save it.
blog/models.py
class Post(models.Model):
author = models.ForeignKey(User, default=1)
title = models.CharField(max_length = 50)
pub_date = models.DateTimeField(default=timezone.now)
content = models.TextField(max_length = 50000)
published = models.BooleanField(default=False)
def __str__(self):
return self.title
def get_absolute_url(self):
return "/blog/%i/" % self.pk
blog/admin.py
class PostAdmin(admin.ModelAdmin):
fieldsets = (
('Title', {'fields' : ['title']}),
('Date', {'fields' : ['pub_date'], 'classes' : ['collapse']}),
('Content', {'fields' : ['content']}),
)
list_display = ('title', 'pub_date')
list_filter = ['pub_date']
search_fields = ['title']
admin.site.register(Post, PostAdmin)
blog/views.py
class PostsListView(ListView):
model = Post
You can modify your list view to only show published posts by overriding get_queryset.
class PostsListView(ListView):
model = Post
def get_queryset(self):
return super(PostsListView, self).get_queryset().filter(published=True)
If you have a detail view, you should override get_queryset in the same way.
In your model admin, you can add published to list_filter. This makes it easy to filter published/unpublished posts.
class PostAdmin(admin.ModelAdmin):
...
list_filter = ['pub_date', 'published']

Adding multiple records into django admin

I would like to be able add multiple records at a time in django admin.
models.py
class Photo(models.Model):
pub_date = models.DateTimeField('date published')
sort = models.IntegerField()
photo_category = models.ForeignKey(Photocategory)
title = models.CharField(max_length=200)
title_url = models.SlugField(max_length=200)
main_image = models.ImageField(blank=True, null=True, upload_to='images/photo/')
# size is "width x height"
cropping = ImageRatioField('main_image', '350x350')
def image_thumbnail(self):
return '<img width="160px" src="/media/%s"/>' % (self.main_image, self.main_image)
image_thumbnail.allow_tags = True
image_thumbnail.short_description = 'main_image'
def __unicode__(self):
return self.title
admin.py
class PhotoAdmin(ImageCroppingMixin, admin.ModelAdmin):
prepopulated_fields = {'title_url': ('title',)}
list_display = ('title', 'photo_category', 'image_thumbnail', 'sort')
list_filter = ['photo_category']
admin.site.register(Photo, PhotoAdmin)
Screenshot:
Is there a way I could do lets say 5 at a time on the 1 screen, it is very slow filling that out 1 by 1. I can do it in mysql with SQL query but I would like to achieve this here.
Thanks
Hey Yes this is possible but Out of the Box,,
Here is what you will have to do:
Define a Formset (to create multiple photo forms) in form Attribute of PhotoAdmin
You will see a formset on the add photo Admin page, and should work correctly. If you don't then you can over-ride the templates by providing add_form_template and change_form_template attributes in Admin and display that form-set templates.
Handle Saving of Multiple objects in the Formset
For example:
# admin.py
#admin.register(Photo)
class PhotoAdmin(ImageCroppingMixin, admin.ModelAdmin):
prepopulated_fields = {'title_url': ('title',)}
list_display = ('title', 'photo_category', 'image_thumbnail', 'sort')
list_filter = ['photo_category']
form = MyFormset
add_form_template = "photo/admin/my_add_form.html"
change_form_template = "photo/admin/my_change_form.html"
This is a rough workflow, you will have to try this and make modification if required.
You can refer to this documentation: https://docs.djangoproject.com/en/1.6/ref/contrib/admin/#modeladmin-options

How do I reference Django Model from another model

Im looking to create a view in the admin panel for a test program which logs Books, publishers and authors (as on djangoproject.com)
I have the following two models defined.
class Author(models.Model):
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=30)
email = models.EmailField()
def __unicode__(self):
return u'%s %s' % (self.first_name, self.last_name)
class Book(models.Model):
title = models.CharField(max_length=100)
authors = models.ManyToManyField(Author)
publisher = models.ForeignKey(Publisher)
publication_date = models.DateField()
def __unicode__(self):
return self.title
What I want to do, is change the Book model to reference the first_name of any authors and show this using admin.AdminModels.
#Here is the admin model I've created.
class BookAdmin(admin.ModelAdmin):
list_display = ('title', 'publisher', 'publication_date') # Author would in here
list_filter = ('publication_date',)
date_hierarchy = 'publication_date'
ordering = ('-publication_date',)
fields = ('title', 'authors', 'publisher', 'publication_date')
filter_horizontal = ('authors',)
raw_id_fields = ('publisher',)
As I understand it, you cannot have two ForeignKeys in the same model. Can anyone give me an example of how to do this?
I've tried loads of different things and its been driving me mad all day. Im pretty new to Python/Django.
Just to be clear - I'd simply like the Author(s) First/Last name to appear alongside the book title and publisher name.
Thanks
You can have more than one Foreign Key on a model.
If you would put a Foreign-Key field's name in list_display you will always just see the __unicode__ representation of the associated model.
But you can add a function like this to your BookAdmin:
def first_names(self, obj):
return ','.join(a.first_name for a in obj.authors.all())
get_sites.short_description = 'First Names'
Then add 'first_names' to list_display.