(haystack + woosh) 'rebuild_index' looking for template in wrong location? - django-haystack

I'm implementing haystack with the whoosh search engine. When I run 'rebuild_index' I get the following error in my terminal.
File "/home/dilts/installingDJANGO/ENV/lib/python3.5/site-packages/django/template/loader.py", line 74, in select_template
raise TemplateDoesNotExist(', '.join(template_name_list), chain=chain)
django.template.exceptions.TemplateDoesNotExist: search/indexes/submit_app/incident_text.txt
And this error in my browser.
reduce() of empty sequence with no initial value
incidents = SearchQuerySet().autocomplete(content_auto=request.POST.get(title, ''))
return clone.filter(six.moves.reduce(operator.__and__, query_bits))
My generic file structure looks like the following...
project
|--------submit_app
|--------search_app (uses a submit_app model called Incident)
|--------templates (where I put search/indexes/search_app/incident_text.txt)
From what I've read online, I believed my structure was correct, but from the error, I'm not so sure anymore. I feel like there might be some confusion with the shared model, but I don't know.
settings.py
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
WHOOSH_INDEX = os.path.join(BASE_DIR,'whoosh/')
HAYSTACK_CONNECTIONS = {
'default':{
'ENGINE': 'haystack.backends.whoosh_backend.WhooshEngine',
'PATH': WHOOSH_INDEX,
},
}
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(BASE_DIR, 'templates')],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
views.py
from django.shortcuts import render
from .forms import IncidentsSearchForm
from django.contrib.auth.decorators import login_required
from haystack.query import SearchQuerySet
#login_required(login_url='/login/')
def incidents(request):
if request.method == 'POST': # If the form has been submitted...
form = IncidentsSearchForm(request.POST) # A form bound to the POST data
if form.is_valid(): # All validation rules pass
title = form.cleaned_data['title']
## this piece isn't working at I had hoped
incidents = SearchQuerySet().autocomplete(content_auto=request.POST.get(title, ''))
else:
form = IncidentsSearchForm() # An unbound form
title = ''
incidents = ''
return render(request, 'search.html',
{
'form': form,
'title': title,
'incidents' : incidents,
}
)

when you include('haystack.urls'), it will look search/search.html by default. Are you have a search.html file ?
/templates/search/search.html
views.py
return render(request, 'search/search.html',
{
'form': form,
'title': title,
'incidents' : incidents,
}

I had the same issue with rebuild_index. It seems that Haystack looks for the txt template inside the model's app folder. In your case you should put the index in
templates/search/indexes/submit_app/incident_text.txt
submit_app not search_app

Related

How to get signals.py executed in django?

I run the following signals.py on (Ubuntu2204/WSL2 Windows 11) using Django 4.1.1 / Python 3.10
from .models import Sale
from django.db.models.signals import m2m_changed
from django.dispatch import receiver
#receiver(m2m_changed, sender=Sale.positions.through)
def calculate_total_price(sender, instance, action, **kwargs):
print('action', action)
total_price = 0
if action == 'post_add' or action == 'post_remove':
for item in instance.get_positions():
total_price += item.price
instance.total_price = total_price
instance.save()
apps.py already signals in VSCode that signals is not used, this is also confirmed in the Django Debug Toolbar
from django.apps import AppConfig
class SalesConfig(AppConfig):
#default_auto_field = 'django.db.models.BigAutoField'
name = 'sales'
def ready(self):
import sales.signals
and then the __init__.py file
default_app_config = 'sales.apps.SalesConfig'
settings.py looks like this:
"""
Django settings for reports_proj project.
Generated by 'django-admin startproject' using Django 4.1.1.
For more information on this file, see
https://docs.djangoproject.com/en/4.1/topics/settings/
For the full list of settings and their values, see
https://docs.djangoproject.com/en/4.1/ref/settings/
"""
from pathlib import Path
# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/4.1/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = "django-insecure-dbl^hk-m6-dz01+1i*hi5#rz4t90(y5s-cls1&0js5hj^ojo70"
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
ALLOWED_HOSTS = []
# Application definition
INSTALLED_APPS = [
"django.contrib.admin",
"django.contrib.auth",
"django.contrib.contenttypes",
"django.contrib.sessions",
"django.contrib.messages",
"django.contrib.staticfiles",
"debug_toolbar",
# my apps
"customers",
"products",
"profiles",
"reports",
"sales.apps.SalesConfig",
# 3rd party
"crispy_forms",
]
CRISPY_TEMPLATE_PACK = "bootstrap4"
MIDDLEWARE = [
"django.middleware.security.SecurityMiddleware",
"django.contrib.sessions.middleware.SessionMiddleware",
"django.middleware.common.CommonMiddleware",
"django.middleware.csrf.CsrfViewMiddleware",
"debug_toolbar.middleware.DebugToolbarMiddleware",
"django.contrib.auth.middleware.AuthenticationMiddleware",
"django.contrib.messages.middleware.MessageMiddleware",
"django.middleware.clickjacking.XFrameOptionsMiddleware",
]
ROOT_URLCONF = "reports_proj.urls"
TEMPLATES = [
{
"BACKEND": "django.template.backends.django.DjangoTemplates",
"DIRS": [BASE_DIR / 'templates'],
"APP_DIRS": True,
"OPTIONS": {
"context_processors": [
"django.template.context_processors.debug",
"django.template.context_processors.request",
"django.contrib.auth.context_processors.auth",
"django.contrib.messages.context_processors.messages",
],
},
},
]
WSGI_APPLICATION = "reports_proj.wsgi.application"
# Database
# https://docs.djangoproject.com/en/4.1/ref/settings/#databases
DATABASES = {
"default": {
"ENGINE": "django.db.backends.sqlite3",
"NAME": BASE_DIR / "db.sqlite3",
}
}
# Password validation
# https://docs.djangoproject.com/en/4.1/ref/settings/#auth-password-validators
AUTH_PASSWORD_VALIDATORS = [
{
"NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator",
},
{
"NAME": "django.contrib.auth.password_validation.MinimumLengthValidator",
},
{
"NAME": "django.contrib.auth.password_validation.CommonPasswordValidator",
},
{
"NAME": "django.contrib.auth.password_validation.NumericPasswordValidator",
},
]
# Internationalization
# https://docs.djangoproject.com/en/4.1/topics/i18n/
LANGUAGE_CODE = "en-us"
TIME_ZONE = "UTC"
USE_I18N = True
USE_TZ = True
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/4.1/howto/static-files/
STATIC_URL = '/static/'
STATICFILES_DIRS = [
BASE_DIR / 'static',
BASE_DIR / 'sales' / 'static',
]
MEDIA_URL = '/media/'
MEDIA_ROOT = BASE_DIR / 'media'
# Default primary key field type
# https://docs.djangoproject.com/en/4.1/ref/settings/#default-auto-field
DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
INTERNAL_IPS = [
"127.0.0.1",
]
Any hint would be highly appreciated, the code looks ok to me it must be some dependency issue but I have no idea where to start looking really
I just noticed that your ready identation is not correct, causing the problem...
You should change this:
from django.apps import AppConfig
class SalesConfig(AppConfig):
#default_auto_field = 'django.db.models.BigAutoField'
name = 'sales'
def ready(self):
import sales.signals
To this:
from django.apps import AppConfig
class SalesConfig(AppConfig):
#default_auto_field = 'django.db.models.BigAutoField'
name = 'sales'
def ready(self):
from . import signals
Generally because no indentation the method became global function and it was not called as it should.

How to include Hyperlinks for nested resources in drf-nested-routers to apply HATEOAS principle?

Context
I have an API in Django REST framework with the following nested resources
/wizard-api/industries/
/wizard-api/industries/<pk>/
/wizard-api/industries/<industry_pk>/sub-industries/
/wizard-api/industries/<industry_pk>/sub-industries/<pk>/
/wizard-api/industries/<industry_pk>/sub-industries/<sub_industry_pk>/details/
/wizard-api/industries/<industry_pk>/sub-industries/<sub_industry_pk>/details/<pk>/
# basenames:
wizard-api:industries-list
wizard-api:industries-detail
wizard-api:sub-industries-list
wizard-api:sub-industries-detail
wizard-api:details-list
wizard-api:details-detail
Here my URLs config using drf-nested-routers:
# Nested Routes
first_level = routers.SimpleRouter()
first_level.register(r'industries', views.IndustryViewSet, basename='industries')
second_level = routers.NestedSimpleRouter(first_level, r'industries', lookup='industry')
second_level.register(r'sub-industries', views.SubIndustryViewSet, basename='sub-industries')
third_level = routers.NestedSimpleRouter(second_level, r'sub-industries', lookup='sub_industry')
third_level.register(r'details', views.SubIndustryDetailsViewSet, basename='abc')
ERD
I want to apply the HATEOAS principle
# endpoint: /wizard-api/industries/1/
# response:
{
"id": 1,
"name": "food and beverage",
"subindustries": "http://127.0.0.1:8000/wizard-api/industries/1/sub-industries/"
}
I made it for the first level using the HyperlinkedIdentityField in the first Serializer
class IndustryModelSerializer(serializers.ModelSerializer):
subindustries = serializers.HyperlinkedIdentityField(
view_name='wizard-api:sub-industries-list',
lookup_url_kwarg='industry_pk'
)
class Meta:
model = Industry
exclude = ['created', 'modified', 'active']
Problem
The problem appears when I try to apply the same logic in the subsequent levels, i.e: generating the url from the sub-industries level to the details level:
/wizard-api/industries/<industry_pk>/sub-industries/<sub_industry_pk>/details/
I tried with the details field in the serializer of the second level (sub-industries):
class SubIndustryModelSerializer(serializers.ModelSerializer):
details = serializers.HyperlinkedIdentityField(
view_name='wizard-api:details-list',
lookup_url_kwarg='industry_pk'
)
class Meta:
model = SubIndustry
exclude = ['created', 'modified', 'active']
Expected
The expected response is:
# endpoint: /wizard-api/industries/1/sub-industries/
# response:
[
{
"id": 1,
"name": "beverage industries",
"details": "http://127.0.0.1:8000/wizard-api/industries/1/sub-industries/1/details/"
},
{
"id": 2,
"name": "food production",
"details": "http://127.0.0.1:8000/wizard-api/industries/1/sub-industries/2/details/"
}
]
Error
But I got the following error:
Could not resolve URL for hyperlinked relationship using view name
"wizard-api:details-list". You may have failed to include the related
model in your API, or incorrectly configured the lookup_field
attribute on this field.
I made some tests and found the solution.
First, the best way to implement the nested routes in your case will be with these HyperlinkedModelSerializer, NestedHyperlinkedModelSerializer.
So the first level should look like this:
class IndustryModelSerializer(HyperlinkedModelSerializer):
subindustries = serializers.HyperlinkedIdentityField(
view_name='wizard-api:sub-industries-list',
lookup_url_kwarg='industry_pk'
)
class Meta:
model = Industry
fields = ['subindustries', 'foo'...]
The main problem in the second level can be fixed like this:
class SubIndustryModelSerializer(NestedHyperlinkedModelSerializer):
parent_lookup_kwargs = {
'industry_pk': 'industry_id'
}
class Meta:
model = SubIndustry
fields = ['foo', 'url', ...]
extra_kwargs = {
'url': {
'view_name': 'wizard-api:details-list',
'lookup_url_kwarg': 'sub_industry_pk'
}
}
Based on your ERD, you must include a parent_kwargs for the proper configuration of nested routes, change the exclude for fields, and add the url parameter.
Hope it works!

How to change `DIRS` path dynamically in Django?

I want to change DIRS dynamically.based on devices.
if request.user_agent.is_pc:
request.template_prefix = 'desktop'
else:
request.template_prefix = 'mobile'
Default (settings.py):
TEMPLATES = [
{
'DIRS': ['templates'],
},
]
I want to change my DIRS path like this (settings.py):
TEMPLATES = [
{
'DIRS': [f"templates/{request.template_prefix}"],
},
]
Also let me know if you need more codes.
Note: I can't use user_agent in settings.py Because it requires a request.
that's why I asked.
My django version is: 3.2.x
In simple words: How to change DIRS path in views.py.
Thanks!

How can I include a Count in my Django Rest Framework Pagination to count the total number of rows?

I have written a code in Python using Django Rest Framework to fetch data from database(MySql) and view it in browser using Pagination(PageNumberPagination).It is working fine but the problem is the "count" is not coming in view. I want to show the total number of rows in count. I am getting response like this:
GET /fetchdata/?page=1&per_page=10
HTTP 200 OK
Allow: GET, OPTIONS, POST
Content-Type: application/json
Vary: Accept
[
{
"id": 53,
"first_name": "Rounak",
},
{
"id": 54,
"first_name": "Rounak",
},
]
But I want the response like this.
HTTP 200 OK
{
"count": 1023
"next": "https://api.example.org/persons/?page=2&per_page=10",
"previous": null,
"results": [
…
]
}
Below is my code, please suggest me something.
view.py
from rest_framework.pagination import PageNumberPagination
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
from .pagination import StandardResultsSetPagination
#api_view(['GET','POST'])
def index(request):
if request.method=='GET':
all_dataobj=fetchdata.objects.all()
paginator = StandardResultsSetPagination()
result_page = paginator.paginate_queryset(all_dataobj, request)
pserializer=fetchdataSerializers(result_page,many=True)
return Response(pserializer.data,status=status.HTTP_201_CREATED)
elif request.method=='POST':
serializer=fetchdataSerializers(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data,status=status.HTTP_201_CREATED)
return Response(serializer.errors,status=status.HTTP_400_BAD_REQUEST)
pagination.py
from rest_framework import pagination
class StandardResultsSetPagination(pagination.PageNumberPagination):
page_size = 10
page_query_param = 'page'
page_size_query_param = 'per_page'
max_page_size = 1000
settings.py
REST_FRAMEWORK = {
'DEFAULT_PAGINATION_CLASS':
'rest_framework.pagination.PageNumberPagination',
'PAGE_SIZE': 10
}

flask-marshmallow custom fields

I use flask-marshmallow and mongoengine.
Also flask-restplus for my API server.
Here is my api.py
class BoardSchema(ma.Schema):
class Meta:
fields = ('no', 'title', 'body', 'tags', 'created_at', 'views')
board_schema = BoardSchema()
boards_schema = BoardSchema(many=True)
class ArticleList(Resource):
def get(self):
articles = Board.objects.all()
return boards_schema.jsonify(articles)
model.py
from datetime import datetime
from mongoengine import *
from config import DB_NAME
connect(DB_NAME)
class Board(Document):
d = datetime.now()
date = "{}-{}-{}".format(d.year, d.month, d.day)
no = SequenceField()
title = StringField(required=True)
body = StringField(required=True)
tags = ListField(StringField())
likes = ListField(StringField())
views = ListField(StringField())
password = StringField(required=True)
created_at = DateTimeField(default=date)
updated_at = DateTimeField(default=date)
When I access to /article, it's result like this ->
{
"body": "123",
"created_at": "2018-08-20T00:00:00+00:00",
"no": 1,
"tags": [
"MySQL",
"C"
],
"title": "\ud14c\uc2a4\ud2b8",
"views": [
"127.0.0.1"
]
}
in "views", ip will be added who read article.
But I want to count of all the list of views and include it to my result.
The result I wanted is here.
{
"body": "123",
"created_at": "2018-08-20T00:00:00+00:00",
"no": 1,
"tags": [
"MySQL",
"C"
],
"title": "\ud14c\uc2a4\ud2b8",
"views": 20
}
I'm new at flask-marshmallow so I'm so confused how can I solve this issue.
Thanks.
Maybe you can try like this:
class BoardSchemaCustom(ma.ModelSchema):
class Meta:
model = Board
views = ma.fields.method(deserialize="_custom_serializer")
def _custom_serializer(self, obj):
return len(obj.views)
Create instance of your custom schema:
custom_board_schema = BoardSchemaCustom()
and dump it:
dump, errors = custom_board_schema.schema.dump(Board.query.first())
>>> dump
i've got the same problem. and my code works after installing marshmallow-sqlalchemy
pip install marshmallow-sqlalchemy
see from offical documentation
https://flask-marshmallow.readthedocs.io/en/latest/
Below snippet would also work:
class BoardSchemaCustom(ma.ModelSchema):
class Meta:
model = Board
views = ma.fields.Function(lambda obj: len(obj.views))

Resources