Django: Set many-to-many value to a model - django

I am trying to test my Models Project, Category and Tag. I'm running into an issue when trying to add tags to my project model.
It won't allow me to do it in the Project model itself for eg.
self.project = Project.objects.create(
...
tags=Tag.objects.create("HTML5"),
)
Django docs suggest the I do it as below. However I can't "add" the Tag without saving the model and I can't save the model without adding the Tag
Tests
class ProjectTests(TestCase):
def setUp(self):
self.tag = Tag.objects.create(name="HTML5")
self.project = Project(
title="Oaks on Main Shopping Center",
url="www.oaksonmain.co.za",
image=SimpleUploadedFile(
name="test-image.jpg",
content=open(
"static\\images\\test_images\\florian-olivo-4hbJ-eymZ1o-unsplash (1).jpg", "rb"
).read(),
content_type="image/jpeg",
),
description="Beautiful website created for Oaks on Main Shopping Center in Knysna!",
category=Category.objects.create(name="Website"),
)
self.project.save() <- Problem here
self.project.tags.add(self.tag) <- Problem here
def test_project_model(self):
self.assertEqual(f"{self.project.title}", "Oaks on Main Shopping Center")
self.assertEqual(f"{self.project.url}", "www.oaksonmain.co.za")
self.assertEqual(
f"{self.project.description}",
"Beautiful website created for Oaks on Main Shopping Center in Knysna!",
)
self.assertEqual(self.tags.count(), 3)
self.assertEqual(self.category.count(), 1)
self.assertEqual(self.image.count(), 1)
def test_project_listview(self):
resp = self.client.get(reverse("index"))
self.assertEqual(resp.status_code, 200)
self.assertContains(resp, self.project.title)
self.assertTemplateUsed(resp, "page/index.html")
Models
class Project(models.Model):
class Meta:
ordering = ["-id"] # Always show latest projects first
verbose_name_plural = "Projects"
title = models.CharField(max_length=50)
url = models.URLField()
image = models.ImageField(upload_to=f"{title}/")
description = models.TextField()
category = models.ForeignKey("Category", on_delete=models.PROTECT, related_name="categories")
tags = models.ManyToManyField("Tag", verbose_name="tags")
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse("index")
class Category(models.Model):
class Meta:
ordering = ["name"]
verbose_name_plural = "Categories"
name = models.CharField(max_length=20)
def __str__(self):
return self.name
def get_absolute_url(self):
return reverse("index")
class Tag(models.Model):
class Meta:
ordering = ["name"]
verbose_name_plural = "Tags"
name = models.CharField(max_length=10)
def __str__(self):
return self.name
def get_absolute_url(self):
return reverse("index")

You can first save the tag or the model object, and then later you link the tag as (one of) tags of that Project, so:
html5_tag, __ = Tag.objects.get_or_create(name='HTML5')
self.project = Project.objects.create(
# no tags=…
)
self.project.tags.add(html5_tag)
Your ImageField also should work with a callable for the upload_to=… parameter, so:
class Project(models.Model):
# …
def upload_image(self, filename):
return f'{self.title}/{filename}'
image = models.ImageField(upload_to=upload_image)
# …

Related

showing entries based on selection type in django

my question is i want to show only particular titles under music_track (musicmodel)field when type = track(title model) in my django admin site
class album(models.Model):
def get_autogenerated_code():
last_id = album.objects.values('id').order_by('id').last()
if not last_id:
return "AL-"+str(0)
return "AL-"+str(last_id['id'])
album_name = models.CharField( max_length=150, blank=False )
music_track = models.ManyToManyField("title")
def __str__(self):
return (self.album_name)
class Meta:
verbose_name = "Album"
verbose_name_plural = "Albums"
class title(models.Model):
def get_autogenerated_code():
last_id = title.objects.values('id').order_by('id').last()
if not last_id:
return "TT-"+str(0)
return "TT-"+str(last_id['id'])
upc_code = models.CharField(max_length=15, default="N/A", blank=False)
display_name = models.CharField(max_length=150, blank=False)
type = models.ForeignKey(Asset_Type, on_delete=models.CASCADE, null=True)
def __str__(self):
return (self.display_name+ " " +self.code)
admin.site.register( [album, title] )
From your question, I am understanding that while creating an album in your admin panel, you require that the music_track should only show the titles having type as track. My solution for this is:
In your admin.py file
from .models import title, album, Asset_type
class AlbumForm(forms.ModelForm):
class Meta:
model = Product
fields = ('album_name', 'music_track', )
def __init__(self, user, *args, **kwargs):
super(AlbumForm, self).__init__(*args, **kwargs)
type = Asset_type.objects.get(type='track')
self.fields['music_track'].queryset = Title.objects.filter(type=type)
class MyModelAdmin(admin.ModelAdmin):
form = AlbumForm
admin.site.register(album, MyModelAdmin)
Maybe this can give you the idea you need.

Tag search on multiple criterias

I'd like to get all Profile where tag='hello' and tag='world'. I tried with Q() query but I don't have the correct result.
models.py
class Tag(models.Model):
name = models.CharField(unique=True, max_length=100)
slug = models.SlugField(unique=True, max_length=100)
def __str__(self):
return self.name
class Meta:
ordering = ['slug']
class Profile(models.Model):
name = models.CharField(max_length=100)
tags = models.ManyToManyField(Tag)
def __str__(self):
return self.name
class Meta:
ordering = ['name']
views.py
def search(request: HttpRequest):
q_tag_list = request.GET.get('search-tag').split(',')
profile_filter = Q()
for tag in q_tag_list:
profile_filter = profile_filter & Q(tags__slug__startswith=tag)
profiles = Profile.objects.filter(profile_filter)
return render(request, 'list.html', {'profiles': profiles})
sql generated (from django debug toolbar)
SELECT "socialmedia_profile"."id", "socialmedia_profile"."name", "socialmedia_profile"."facebook_name", "socialmedia_profile"."facebook_latest_likes" FROM "socialmedia_profile" INNER JOIN "socialmedia_profile_tags" ON ("socialmedia_profile"."id" = "socialmedia_profile_tags"."profile_id") INNER JOIN "socialmedia_tag" ON ("socialmedia_profile_tags"."tag_id" = "socialmedia_tag"."id") WHERE ("socialmedia_tag"."slug" LIKE '''hello%''' ESCAPE '\' AND "socialmedia_tag"."slug" LIKE '''world%''' ESCAPE '\') ORDER BY "socialmedia_profile"."name" ASC

How to limit choices to Foreign keys in Django admin

I run into a problem when using the Django admin. I'm building a small ScrumBoard. It has projects, with statuses, stories and tasks.
Consider the following model:
#python_2_unicode_compatible
class Project(models.Model):
name = models.CharField(max_length=100)
class Meta:
verbose_name = _('Project')
verbose_name_plural = _('Projects')
def __str__(self):
return self.name
#python_2_unicode_compatible
class Status(models.Model):
name = models.CharField(max_length=64) # e.g. Todo, In progress, Testing Done
project = models.ForeignKey(Project)
class Meta:
verbose_name = _('Status')
verbose_name_plural = _('Statuses')
def __str__(self):
return self.name
#python_2_unicode_compatible
class Story(models.Model):
"""Unit of work to be done for the sprint. Can consist out of smaller tasks"""
project = models.ForeignKey(Project)
name=models.CharField(max_length=200)
description=models.TextField()
status = models.ForeignKey(Status)
class Meta:
verbose_name = _('Story')
verbose_name_plural = _('Stories')
# represent a story with it's title
def __str__(self):
return self.name
The problem: when an admin user creates a story he will see statuses from all the projects instead of the status from one project.
To filter statuses by project, you need your story to already exist so django know which project we are talking about. If you set status nullalble, you can do like this (implying, you do save and continue on first save to set status)
class StatusAdmin(admin.ModelAdmin):
def get_form(self, request, obj=None, **kwargs):
form = super(StatusAdmin, self).get_form(request, obj, **kwargs)
if obj and obj.project:
form.base_fields['status'].queryset = \
form.base_fields['status'].queryset.filter(project=obj.project)
elif obj is None and 'status' in form.base_fields: # on creation
del form.base_fields['status']
return form
You will need something like django-smart-selects

How to surface two Django models on one page?

I would like to create one page that surfaces two separate Django models:
class Client(models.Model):
name = models.CharField(max_length=100)
slug = AutoSlugField(populate_from='name', blank=True, unique=True)
order = models.IntegerField(editable=False, default=0)
class Meta:
ordering = ('order',)
def __unicode__(self):
return self.name
class Press(models.Model):
title = models.CharField(max_length=50)
article = models.ImageField(upload_to = 'images')
def image_thumb(self):
if self.article:
return u'<img src="%s" height="125"/>' %self.article.url
else:
return "no image"
image_thumb.short_description = "article"
image_thumb.allow_tags = True
class Meta:
verbose_name_plural = "press"
I am unsure how to write my queryset in Views.py. I've tried something like this...
class ClientView(generic.ListView):
template_name = 'clients.html'
context_object_name = 'client'
def queryset(request):
client_page = {'press': Press.objects.all(), 'client': Clients.objects.all()}
return client_page
and then this in my urls.py...
url(r'^clients/', views.ClientView.as_view(), name = 'client_model'),
I read in a stack answer that I can do this by using "get_extra_context" but can someone show me how that's used?
class ClientView(generic.ListView):
# ...
def get_context_data(self, **kwargs):
context = super(ClientView, self).get_context_data(**kwargs)
context['press'] = Press.objects.all()
return context

django-piston: how to get values of a many to many field?

I have a model with ManyToManyField to another model. I would like to get all the info on a particular record (including the related info from other models) return by JSON.
How to get django-piston to display those values? I would be happy with just primary keys.
Or can you suggest another option ?
I may be wrong, but this should do it:
class PersonHandler(BaseHandler):
model = Person
fields = ('id', ('friends', ('id', 'name')), 'name')
def read(self, request):
return Person.objects.filter(...)
You need to define a classmethod on the handler that returns the many-to-many data, I don't believe Piston does this automatically.
class MyHandler(BaseHandler):
model = MyModel
fields = ('myfield', 'mymanytomanyfield')
#classmethod
def mymanytomanyfield(cls, myinstance):
return myinstance.mymanytomanyfield.all()
My code:
Models:
class Tag(models.Model):
"""docstring for Tags"""
tag_name = models.CharField(max_length=20, blank=True)
create_time = models.DateTimeField(auto_now_add=True)
def __unicode__(self):
return self.tag_name
class Author(models.Model):
"""docstring for Author"""
name = models.CharField(max_length=30)
email = models.EmailField(blank=True)
website = models.URLField(blank=True)
def __unicode__(self):
return u'%s' % (self.name)
class Blog(models.Model):
"""docstring for Blogs"""
caption = models.CharField(max_length=50)
author = models.ForeignKey(Author)
tags = models.ManyToManyField(Tag, blank=True)
content = models.TextField()
publish_time = models.DateTimeField(auto_now_add=True)
update_time = models.DateTimeField(auto_now=True)
def __unicode__(self):
return u'%s %s %s' % (self.caption, self.author, self.publish_time)
Handle:
class BlogAndTagsHandler(BaseHandler):
allowed_methods = ('GET',)
model = Blog
fields = ('id' 'caption', 'author',('tags',('id', 'tag_name')), 'content', 'publish_time', 'update_time')
def read(self, request, _id=None):
"""
Returns a single post if `blogpost_id` is given,
otherwise a subset.
"""
base = Blog.objects
if _id:
return base.get(id=_id)
else:
return base.all() # Or base.filter(...)
Works petty good.