I would like to apply the autocomplete option for my Preorder.preorder_has_products.through model in order to be able to load products with autocomplete(i tried unsuccessfully).Moreover, I have an inline implementation in order to be able to select for one preorder more than one product. The obstacle is that I have a manytomany field(preorder_has_products) as you can see below and I do not know how to implement the autocomplete.
models.py
class Preorder(models.Model):
client = models.ForeignKey(Client,verbose_name=u'Πελάτης')
preorder_date = models.DateField("Ημ/νία Προπαραγγελίας",null=True, blank=True, default=datetime.date.today)
notes = models.CharField(max_length=100, null=True, blank=True, verbose_name="Σημειώσεις")
preorder_has_products=models.ManyToManyField(Product,blank=True)
def get_absolute_url(self):
return reverse('preorder_edit', kwargs={'pk': self.pk})
class Product(models.Model):
name = models.CharField("Όνομα",max_length=200)
price = models.DecimalField("Τιμή", max_digits=7, decimal_places=2, default=0)
barcode = models.CharField(max_length=16, blank=True, default="")
eopyy = models.CharField("Κωδικός ΕΟΠΥΥ",max_length=10, blank=True, default="")
fpa = models.ForeignKey(FPA, null=True, blank=True, verbose_name=u'Κλίμακα ΦΠΑ')
forms.py
class PreorderHasProductsForm(ModelForm):
product = ModelChoiceField(required=True,queryset=Product.objects.all(),widget=autocomplete.ModelSelect2(url='name-autocomplete'))
class Meta:
model=Preorder.preorder_has_products.through
exclude=('client',)
def __init__(self, *args, **kwargs):
super(PreorderHasProductsForm, self).__init__(*args, **kwargs)
self.fields['product'].label = "Ονομα Προϊόντος"
PreorderProductFormSet = inlineformset_factory(Preorder,Preorder.preorder_has_products.through,form=PreorderHasProductsForm,extra=1)
my views.py for autocomplete
class NameAutocomplete(autocomplete.Select2QuerySetView):
def get_queryset(self):
# Don't forget to filter out results depending on the visitor !
if not self.request.user.is_authenticated():
return Product.objects.none()
qs = Product.objects.all()
if self.q:
qs = qs.filter(product__istartswith=self.q)
return qs
my template is written based on this tutorial : https://django-autocomplete-light.readthedocs.io/en/master/tutorial.html
and finally my url for autocomplete:
url(r'^name-autocomplete/$',views.NameAutocomplete.as_view(),name='name-autocomplete'),
My result based on the above snippets is depicted in the attached image.
what could be wrong? I guess one possible issue could be the reference to the manytomanyfield table. Any idea?
Related
I have a ListView as follows, enabling me to loop over two models (Market and ScenarioMarket) in a template:
class MarketListView(LoginRequiredMixin, ListView):
context_object_name = 'market_list'
template_name = 'market_list.html'
queryset = Market.objects.all()
login_url = 'login'
def get_context_data(self, **kwargs):
context = super(MarketListView, self).get_context_data(**kwargs)
context['scenariomarkets'] = ScenarioMarket.objects.all()
context['markets'] = self.queryset
return context
The two market models are as follows:
class Market(models.Model):
title = models.CharField(max_length=50, default="")
current_price = models.DecimalField(max_digits=5, decimal_places=2, default=0.50)
description = models.TextField(default="")
shares_yes = models.IntegerField(default=0)
shares_no = models.IntegerField(default=0)
b = models.IntegerField(default=100)
cost_function = models.IntegerField(default=0)
open = models.BooleanField(default=True)
def __str__(self):
return self.title[:50]
def get_absolute_url(self):
return reverse('market_detail', args=[str(self.id)])
class ScenarioMarket(models.Model):
title = models.CharField(max_length=50, default="")
description = models.TextField(default="")
b = models.IntegerField(default=100)
cost_function = models.IntegerField(default=0)
most_likely = models.CharField(max_length=50, default="Not defined")
open = models.BooleanField(default=True)
def __str__(self):
return self.title[:50]
def get_absolute_url(self):
return reverse('scenario_market_detail', args=[str(self.id)])
And my user model is as follows:
class CustomUser(AbstractUser):
points = models.DecimalField(
max_digits=20,
decimal_places=2,
default=Decimal('1000.00'),
verbose_name='User points'
)
bets_placed = models.IntegerField(
default=0,
verbose_name='Bets placed'
)
net_gain = models.DecimalField(
max_digits=20,
decimal_places=2,
default=Decimal('0.00'),
verbose_name='Net gain'
)
class Meta:
ordering = ['-net_gain']
What I want happen is that different users see different sets of markets. For example, I want users from company X to only see markets pertaining to X, and same for company Y, Z, and so forth.
Four possibilities so far, and their problems:
I could hardcode this: If each user has a company feature (in addition to username, etc.), I could add a company feature to each market as well, and then use if tags in the template to ensure that the right users see the right markets. Problem: Ideally I'd want to do this through the Admin app: whenever a new market is created there, it would be specified what company can see it.
I could try to use Django's default permissions, which of course would be integrated with Admin. Problem: Setting a view permission (e.g., here) would concern the entire model, not particular instances of it.
From googling around, it seems that something like django-guardian might be what I ultimately have to go with. Problem: As I'm using a CustomUser model, it seems I might run into problems there (see here).
I came across this here on SO, which would enable me to do this without relying on django-guardian. Problem: I'm not clear on how to integrate that into the Admin app, in the manner that django-guardian seems able to.
If anyone has any advice, that would be greatly appreciated!
You can add some relationships between the models:
class Company(models.Model):
market = models.ForeignKey('Market', on_delete=models.CASCADE)
...
class CustomUser(AbstractUser):
company = models.ForeignKey('Company', on_delete=models.CASCADE)
...
then in your view you can simply filter the queryset as appropriate:
class MarketListView(LoginRequiredMixin, ListView):
context_object_name = 'market_list'
template_name = 'market_list.html'
login_url = 'login'
def get_queryset(self):
return Market.objects.filter(company__user=self.request.user)
Note, you don't need the context['markets'] = self.queryset line in your get_context_data; the queryset is already available as market_list, since that's what you set the context_object_name to.
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.
I have this models:
class Country(models.Model):
name = models.CharField(max_length=250)
def __str__(self):
return str(self.name)
class City(models.Model):
name = models.CharField(max_length=250)
country = models.ForeignKey(Country, default=None, blank=True)
def __str__(self):
return str(self.name)
class Airport(models.Model):
name = models.CharField(max_length=250)
city = models.ForeignKey(City, default=None, blank=True)
def __str__(self):
return "{0} - {1} - {2}".format(self.city, self.city.country, self.name)
class Tour(models.Model):
title = models.CharField(max_length=200)
tour_from = models.ForeignKey(Airport)
tour_to = models.ForeignKey(Airport)
def __str__(self):
return str(self.title)
For string representation of Airport Django sends many requests to DB:
302.06 ms (591 queries including 586 similar and 586 duplicates )
Queries screenshot:
At tour/create page I have a ModelForm for creating a tour and Django sends these queries for displaying form.
forms.py:
class TourCreateForm(forms.ModelForm):
class Meta:
model = Tour
fields = ['title', 'tour_from', 'tour_to']
views.py:
class DashboardTourCreate(CreateView):
model = Tour
template_name = "dashboard/tour/create.html"
form_class = TourCreateForm
def get_context_data(self, **kwargs):
context = super(DashboardTourCreate, self).get_context_data(**kwargs)
context['page_name'] = ['tour', 'tour-index']
context['page_title'] = "Create Tour"
return context
How I can reduce queries count?
Root Cause
def __str__(self):
return "{0} - {1} - {2}".format(self.city, self.city.country, self.name)
When the tour_to and tour_from fields are rendered as <option> in the <select> widget the Airport.__str__ method is called. Because Airport.__str__ has self.city.county and both of these are ForeignKey's, the Django ORM issues a query to grab the airports city and the citys country.
And it does this for every single Airport that is an <option> which means the problem will get progressively worse the more Airport's that are added.
Solution
Leverage select_related[1]. select_related will tell the Django ORM to pull in the related fields ('city', 'county') whenever it grabs an Airport.
class TourCreateForm(forms.ModelForm):
class Meta:
model = Tour
fields = ['title', 'tour_from', 'tour_to']
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['tour_from'].queryset = Airport.objects.select_related(
'city__country',
)
self.fields['tour_to'].queryset = Airport.objects.select_related(
'city__country',
)
[1] https://docs.djangoproject.com/en/2.1/ref/models/querysets/#select-related
As f-string is a string literal expressions evaluated at run time link, this might be faster that other string format but i am not fully sure. I am expecting following modification may reduce the over all time.
class Airport(models.Model):
name = models.CharField(max_length=250)
city = models.ForeignKey(City, default=None, blank=True)
def __str__(self):
return f"{self.city} - {self.city.country} - {self.name}"
I fix this issue by adding Queryset to forms.py:
class TourCreateForm(BaseForm):
airports = Airport.objects.select_related('city', 'city__country').all()
tour_from = forms.ModelChoiceField(queryset=airports)
tour_to = forms.ModelChoiceField(queryset=airports)
But I think this is not correct!
I have a Photo model with tags. I want to be able to create a query such that only photos having all search tag terms will be returned. The current behavior (using the view code below) returns photos having any of the tags being searched on.
#list_route(methods=['post'])
def tags(self, request):
"""
search for entities by tags they contain
:param request:
:return: Response
"""
tags = json.loads(request.data['tags'])
photos = Photo.objects.filter(tags__name__in=tags, owner=self.request.user).distinct()
page = self.paginate_queryset(photos)
if page is not None:
serializer = self.get_serializer(page, many=True)
return self.get_paginated_response(serializer.data)
serializer = self.get_serializer(photos, many=True)
return Response(
data=serializer.data
)
Here is the model class:
class Photo(TimestampModerated):
owner = models.ForeignKey('auth.User', related_name='photos', on_delete=models.CASCADE)
uuid = models.UUIDField(default=uuid4, editable=False)
slug = models.SlugField(max_length=80, editable=False)
title = models.CharField(max_length=80, blank=False, null=False)
description = models.TextField(verbose_name='description of entity', blank=True, null=True)
photo = models.ImageField(upload_to=user_directory_path, height_field="height", width_field="width", blank=True)
height = models.IntegerField(blank=True)
width = models.IntegerField(blank=True)
tags = TaggableManager(blank=True)
hash = models.CharField(max_length=64, unique=True, null=True)
class Meta:
verbose_name_plural = "photos"
def __str__(self):
return self.title
def delete(self, using=None, keep_parents=False):
default_storage.delete("{}".format(self.photo))
super().delete()
def save(self, *args, **kwargs):
self.slug = slugify(self.title)
super(Photo, self).save(*args, **kwargs)
I'm using django-rest-framework to create the views and django-taggit for the tagging.
One way is to loop over the list of terms adding filter terms:
photos = Photos.objects.filter(owner=self.request.user)
for tag in tags:
photos = photos.filter(tags__name=tag)
photos = photos.distinct()
Combining Q objects is not suitable here, because as the docs explain Django requires all constraints in a single filter clause to be met by the same related object - ANDing together multiple Q objects would require that one tag have a name equal to each term in the list.
i have 3 models:
class Category(models.Model):
name = models.CharField(max_length=200)
slug = models.SlugField(max_length=70, null=True, blank=True)
class SubCategory(models.Model):
category= models.ForeignKey(Category, on_delete=models.CASCADE)
name = models.CharField(max_length=200, )
class Products(models.Model):
user= models.ForeignKey(User, on_delete=models.CASCADE)
category= models.ForeignKey(Category, on_delete=models.CASCADE)
subcategory = models.CharField(max_length=200, null=True, blank=True)
and i have a view which receive request and category.slug
def category_list(request, slug):
category = Category.objects.get(slug=slug)
products = ProductFilter(request.GET, queryset=Products.objects.filter(category=category)
return render(request, 'products/category_list.html', {"products":products, 'category': category})
when rendering i receive a QuerySet filtered to Category
I want to send category.id to ProductsFilter and recive a dynamic Choices from database
class ProductsFilter(django_filters.FilterSet):
subcategory= django_filters.ChoiceFilter(lookup_expr='iexact', choices=TEST, required=False)
class Meta:
model = Products
fields = {
"subcategory",
}
Want to change choices=TEST to choices=list(SubCategory.objects.filter(category_id=category.id)
Is this possible?
The answer from #Sherpa has just two slight problems. First, you should replace fields with filters. Second, you can't use += operator, you have to directly assign to the filter's extra.
Here's my working code in two different ways
class LayoutFilterView(filters.FilterSet):
supplier = filters.ChoiceFilter(
label=_('Supplier'), empty_label=_("All Suppliers"),)
def __init__(self, *args, **kwargs):
super(LayoutFilterView, self).__init__(*args, **kwargs)
# First Method
self.filters['supplier'].extra['choices'] = [
(supplier.id, supplier.name) for supplier in ourSuppliers(request=self.request)
]
# Second Method
self.filters['supplier'].extra.update({
'choices': [(supplier.id, supplier.name) for supplier in ourSuppliers(request=self.request)]
})
Originally posted here
You can handle this in the FilterSet.__init__ method. Something like the below (Note that I haven't tested it, may require some fiddling):
class ProductsFilter(django_filters.FilterSet):
subcategory= django_filters.ChoiceFilter(lookup_expr='iexact', choices=[], required=False)
def __init__(self, category, *args, **kwargs):
super(ProductsFilter, self).__init__(*args, **kwargs)
choices = self.fields['subcategory'].extra['choices']
choices += [
(subcat.name, subcat.name) for subcat
in SubCategory.objects.filter(category=category)
]
class Meta:
model = Products