I have 2 models Product and Resource. Resource has to be a TabularInline model in my admin panel. I am struggling with filtering resource titles that are related only to this product. Since it is a ForeignKey I should use select_related but I am not sure how to use it in my case. For now, the loop in my HTML file gives me all sales files (from all products).
models.py
class Product(models.Model):
id = models.AutoField(primary_key=True)
title = models.CharField('title', max_length=400, default='')
slug = models.SlugField(unique=True, blank=True, max_length=600)
class Resource(models.Model):
id = models.AutoField(primary_key=True)
type = models.CharField(max_length=32, choices=RESOURCE_TYPE, default='sales')
title = models.CharField(max_length=400, blank=False, null=False, default='')
related_files = models.FileField(upload_to='recources/', null=True, blank=True)
publish = models.DateTimeField('Published on', default=timezone.now)
resources = models.ForeignKey(Product, default='', on_delete=models.PROTECT, null=True, related_name='resources')
admin.py
class Resourceinline(admin.TabularInline):
model = Resource
class ProductAdmin(ImportExportModelAdmin):
inlines = [
Resourceinline,
]
resource_class = ProductResource
admin.site.register(Product, ProductAdmin)
views.py
class ProductDetailView(DetailView):
template_name = 'ProductDetailView.html'
model = Product
def get_context_data(self, **kwargs):
context = super(ProductDetailView, self).get_context_data(**kwargs)
resources_sales = Resource.objects.select_related('resources').filter(resources_id =1, type='sales') # not sure what to put here
context['resources_sales'] = resources_sales
return context
ProductDetailView.html
{% for resource in resources_sales.all %}
<p>{{resource.title}}</p>
{% endfor %}
Question
Where am I making the mistake and how can I display resource objects that are related to type=sales and are related only to this product in DetailView.
Edit
I realized that there is a column named resources_id that is connecting both models. Now I am struggling to filter it by id of current DetailView. I put resources_id=1 in my views.py but it must relate to DetailView that user is currently looking at. I tied to put resources_id=self.kwargs['id'] but it gives me KeyError at /product/test-product/ 'id' How can I do that?
since you are using generic DetailView you can refer to the current object with self.get_object(). actually that return the single object that view display. however you can use instateself.object too.
so you can filter the Product related Resources using Resource.objects.filter(resources=self.get_object(), type='sales')
you can read more Single object mixins
Related
I am trying to save to a model named Blog from inside Django's views.py file. This Blog model is itself linked to the custom user model that I created.
How exactly do I do that? Below are the
models.py file (custom user model is here)
models.py file (Blog model created here - in another Django app)
views.py file where I try to save to the Blog model. How do I reference the user here?
Please excuse the noob-ness of this question. I'm just starting out :)
Inside models.py, I have a custom user model:
class UserExtended(AbstractUser):
is_email_verified = models.BooleanField(default=False)
company = models.CharField(null=True, blank=True, max_length=255)
position = models.CharField(null=True, blank=True, max_length=255)
email = models.EmailField(unique=True)
I also created a model for blog articles in models.py:
class Blog(models.Model):
title = models.CharField(max_length=200)
blogSubject = models.CharField(null=True, blank=True, max_length=200)
keywords = models.CharField(null=True, blank=True, max_length=300)
audience = models.CharField(null=True, blank=True, max_length=200)
# connection to custom user model
profile = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
In views.py, I am trying to save to the Blog model:
def saveBlogTopic(request, blogTopic):
# create a Blog model instance
blog = Blog.objects.create(
title = blogTopic
blogSubject = request.session['blogSubject']
keywords = request.session['keywords']
audience = request.session['audience']
profile = request.user ### ???????? ###
)
I have no idea how to reference the custom user model when saving to the Blog model, which itself is linked via ForeignKey to the custom user model. See last line of code in views.py.
Here is a relationship I'm aiming for in terms of a User, Question, Bookmark relationship; Bookmark being an intermediary table:
A user can bookmark many Questions (topic pages)
A Question (topic page) can be bookmarked by several users
The keyword here being bookmark(ed), I have created a Bookmark model to show this relationship. However there's a problem of trying to make migrations due to a NameError being raised. Depending where they are defined in the script it's raising either:
NameError: name 'Question' is not defined
NameError: name 'Bookmark' is not defined
How can I get past this error in order to push the Bookmark into the migrations directory with its ForeignKey references?
class Question(models.Model):
title = models.CharField(unique=True, max_length=40)
body = models.TextField()
created = models.DateField(auto_now_add=True)
likes = models.IntegerField(default=0)
author = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.SET_NULL,
null=True,
blank=True
)
views = models.ManyToManyField(
View,
related_name="+"
)
tags = models.ManyToManyField(
Tag,
related_name="questions"
)
bookmarks = models.ManyToManyField(
Bookmark,
related_name="+",
)
def __str__(self):
return self.title
class Bookmark(models.Model):
question = models.ForeignKey(
Question, on_delete=models.CASCADE,
related_name="+"
)
user = models.ForeignKey(
settings.AUTH_USER_MODEL, on_delete=models.CASCADE,
related_name="bookmarks"
)
Pass the related model as a string to the field constructor:
bookmarks = models.ManyToManyField(
'Bookmark',
related_name="+",
)
Django looks up the model class only when it's necessary, so it can support recursive foreign key shenanigans.
From the Django documentation:
If you need to create a relationship on a model that has not yet been defined, you can use the name of the model, rather than the model object itself.
UPDATE: I created a github repo with a full site demonstration of the problem.
Maybe my description below isn't quite communicating what I'm trying to do.
The github repo is: https://github.com/theCodeJerk/m2m-through
I really appreciate any help you may offer.
The code below is stripped down to illustrate the issue. While there are things that you may want to say "why would you do this anyway", there is probably a reason in the larger context :)
Here is my view:
class SubmissionCreate(CreateView):
model = Submission
fields = '__all__'
template_name_suffix = '_create_form'
success_url = '/'
Here is the relevant models.py code:
def custom_filename(instance, filename):
author = instance.publishers[0]
return 'papers/{0}.pdf'.format(author.pseudonum)
class Submission(models.Model):
name = models.CharField(
max_length=200,
blank=False
)
upload = models.FileField(
blank=True,
upload_to=custom_filename
)
publishers = models.ManyToManyField(
'Publisher',
blank=False,
related_name='publisher_of',
through='SubmissionPublisher'
)
class Publisher(models.Model):
user = models.ForeignKey(
User, blank=False,
on_delete=models.CASCADE
)
pseudonym = models.CharField(
max_length=200,
blank=False
)
class SubmissionPublisher(models.Model):
publisher = models.ForeignKey(
'Publisher',
blank=False,
on_delete=models.CASCADE
)
submission = models.ForeignKey(
'Submission',
blank=False,
on_delete=models.CASCADE
)
The problem is in the custom_filename, because I need the first publisher from the instance to generate the filename. The Submission is not yet saved when the SubmissionPublisher needs it to be saved.
What would the best way to do this be. Hopefully I have made sense here.
Thanks for any help!
Probably you can try like this:
First, update your custom_filename method:
def custom_filename(instance, filename):
if instance:
authors = instance.publishers.all()
if authors.exists():
author = authors[0]
return 'papers/{0}.pdf'.format(author.pseudonum)
return filename
Here I have fixed few issues, for example in your code instances.publishers[0] won't work, because you need to use a queryset method(like all(), or filter() etc) to access Publisher instances.
Then, make upload field nullable. Because you can't create M2M relations without creating Submission instance, and you can't create Submission instance with upload not null, because it requires an image.
class Submission(models.Model):
name = models.CharField(
max_length=200,
blank=False
)
upload = models.FileField(
null=True, default=None,
blank=True,
upload_to=custom_filename
)
Then, create a Form and override the save method:
from django import forms
from .models import Submission
class SubmissionForm(forms.ModelForm):
class Meta:
model = Submission
fields = '__all__'
def save(self, commit=True):
uploaded_file = self.cleaned_data.pop('upload')
instance = super().save(commit=True)
instance.upload = uploaded_file
instance.save()
return instance
Here I am pulling out the value for upload and saving the instance first. Then putting the image later. This code will work because upload field is nullable in your Submission model.
Finally, use that form class in your SubmissionCreate view:
class SubmissionCreate(CreateView):
model = Submission
form_class = SubmissionForm
template_name_suffix = '_create_form'
success_url = '/'
I have the following models:
class Project(models.Model):
name = models.CharField(max_length=300, unique=True)
description = models.CharField(max_length=2000)
class TemporaryUser(models.Model):
username = models.CharField(max_length=400)
project = models.ForeignKey(
Project,
on_delete=models.CASCADE,
related_name='users'
)
class QuestionSession(models.Model):
project = models.ForeignKey(
Project,
on_delete=models.CASCADE,
related_name='sessions',
blank=True,
null=True,
default=None
)
class Question(models.Model):
# stores the main json object with all required information
description = JSONField(
max_length=10000, blank=True, null=True, default=None
)
question_session = models.ForeignKey(
QuestionSession,
on_delete=models.CASCADE,
related_name='questions',
blank=True,
null=True,
default=None
)
class Answer(models.Model):
question = models.ForeignKey(
Question,
related_name='answers_list',
on_delete=models.CASCADE)
answer = models.CharField(max_length=500)
answered_by = models.ForeignKey(
TemporaryUser,
on_delete=models.CASCADE,
related_name='answers',
blank=True,
null=True,
default=None
)
In a nutshell, my app contains questions, session is a collection of questions, and a project is a collection of sessions. All users are unique per project.
I can fetch all users and all answers within a specific project with the following:
TemporaryUser.objects.all().filter(project__id=project_id)
How can I do the same within a session? I don't really know how to do it, I need to filter users by session, is there a way how to do it with my relations?
Do you mean like:
TemporaryUser.objects.filter(project__sessions__id=id)
I think its cleaner to use reverse relation here:
session = QuestionSession.objects.first()
session.project.users.all()
# as User model has a FK with Project , and it has related_name="users"
You can use this in your template as well:
{% for qs in questionsessions %} // questionsessions is the queryset of QuestionSession
{% for user in qs.project.users.all %}
{{ user.username }}
{% endfor %}
{% endfor %}
If you want to fetch the users with only one query, the way to go is:
users = TemporaryUser.objects.filter(project__sessions=id)
However, if you want to fetch more data related to that session, maybe you should consider to fetch from the session itself (see Following relationships backward). Be cautious, as the number of queries to the database is not optimized.
session = Session.objects.get(pk=id)
users = session.project.users.all()
questions = session.questions.all()
You can use select_related and prefetch_related if you want to make less queries. This could be very important if you are interested in fetching data for a list of sessions and not only one.
I'm having trouble overriding the formset on a TabularInline inline of a ModelAdmin object in my admin site. I know you're supposed to have a model associated with a TabularInline object, but I'm not sure how to specify this on the form object used to generate the formset. With the code below, I'm getting "'AppAssetInline.formset' does not inherit from BaseModelFormSet."
class AppAssetForm(forms.ModelForm):
model = App.assets.through
primary = forms.BooleanField()
uuid = forms.CharField()
class AppAssetInline(admin.TabularInline):
model = App.assets.through
AssetFormset = formset_factory(AppAssetForm)
formset = AssetFormset
class AppAdmin(admin.ModelAdmin):
inlines = [AppAssetInline,]
The answer to my question didn't have to do with how I was structuring my forms, but rather how I was joining fields on my models. I had the following structure in my models:
class App(models.Model):
package = models.FileField(upload_to=settings.APP_PACKAGE_ROOT)
assets = models.ManyToManyField('AppAsset', blank=True, null=True)
download_count = models.IntegerField(default=0)
class AppAsset(models.Model):
def __unicode__(self):
return self.asset_file.name
notes = models.CharField(max_length=255, null=True, blank=True)
type = models.CharField(max_length=255, null=True, blank=True)
asset_file = models.FileField(upload_to=settings.APP_PACKAGE_ROOT)
What I did was change the structure such that AppAsset now has a foreign key on App for its assets. After that, I could use the TabularInline on the AppAsset model with no problems. Here are the latest source files:
https://github.com/ridecharge/spout/blob/master/Spout/AppDistribution/models.py
https://github.com/ridecharge/spout/blob/master/Spout/AppDistribution/admin.py
You should use django.forms.models.inlineformset_factory instead of formset_factory