ForeignKeyAutocompleteAdmin - django

Given these two models:
class Product(Model):
upc = CharField(max_length=96, unique=True)
...
class Meta:
app_label = 'scrapers'
class Order(Model):
...
product = ForeignKey('scrapers.Product', related_name='orders', on_delete=models.CASCADE)
objects = OrderManager()
class Meta:
app_label = 'scrapers'
And this admin.py:
class OrderAdmin(ForeignKeyAutocompleteAdmin):
related_search_fields = {
'product': ('upc', 'retailer_offerings__name')
}
fields = ('product', 'client', 'completed', 'expires', 'amount', 'filled')
admin.site.register(Order, OrderAdmin)
Having done collectstatic and declared django_extensions and my app in INSTALLED_APPS. Why am I getting this:
[04/Dec/2016 05:54:28] "GET /admin/scrapers/product/foreignkey_autocomplete/?search_fields=upc&app_label=scrapers&model_name=product&q=045496 HTTP/1.1" 302 0
Not Found: /admin/scrapers/product/foreignkey_autocomplete/change/
[04/Dec/2016 05:54:28] "GET /admin/scrapers/product/foreignkey_autocomplete/change/ HTTP/1.1" 404 1875
On input to the input field (box to the left not pk input to the right)?
The Product table has millions of rows and the default admin configuration doesn't handle this well so I tried the extensions package solution. The widget is requesting product/foreignkey_autocomplete but a quick grep through manage.py show_urls shows that only /orders has been registered with the foreignkeyautocomplete package. I don't see anything in the docs addressing url configuration (I assume this is done when registering with the admin). How can I get this to work?
Partial solution:
after examining the urls and realizing it was attempting top send the search queries to /product/foreignkey_autocomplete/... I tried to create an empty admin for that model as well. It worked but the behavior is still odd. It seems to stop searching after 4-5 characters have been entered and doesn't bother refreshing.

as per my update, adding a ForeignKeyAutocompleteAdmin for the other side of the relation created the missing urls and the functionality seems to work

#Verbal_Kint I think I may have figured the same issue as yours.
I have a InlineModelAdmin "TaskInline" for model Task, a field "SCRIPT" of it is a foreign-key to model TestScript (managed by "TestAdmin").
After I made sure the related model's ModelAdmin (here for me it's TestAdmin) inherits ForeignKeyAutoCompleteAdmin instead of admin.ModelAdmin, then made sure TestAdmin had an method wrap as below:
class TestAdmin(ForeignKeyAutocompleteAdmin):
ForeignKeyAutocompleteAdmin.model = TestScript
def wrap(self, view):
def wrapper(*args, **kwargs):
return self.admin_site.admin_view(view)(*args, **kwargs)
wrapper.model_admin = self
return update_wrapper(wrapper, view)
def get_urls(self):
info = self.model._meta.app_label, self.model._meta.model_name
urlpatterns = super(TestAdmin, self).get_urls()
urlpatterns.insert(0, url(r'^(.+)/run/', self.wrap(self.run_view), name='%s_%s_run' % info))
urlpatterns.insert(0, url(r'^add/$', self.wrap(self.add_view), name='%s_%s_add' % info))
urlpatterns.insert(0, url(r'^add_to_template_mission/$', self.wrap(self.add_to_template_mission_view), name='%s_%s_add_to_template_mission' % info))
urlpatterns.insert(0, url(r'^add_to_mission/$', self.wrap(self.add_to_mission_view), name='%s_%s_add_to_mission' % info))
urlpatterns.insert(0, url(r'^$', self.wrap(self.changelist_view), name='%s_%s_changelist' % info))
return urlpatterns
class TaskInline(ForeignKeyAutocompleteTabularInline):
model = Task
related_search_fields = {
'SCRIPT': ('FILENAME', 'FILE_PATH', 'FILE_CONTENT', ),
}
And, don't forget to have
urlpatterns = super(TestAdmin, self).get_urls()
in get_urls() inside TestAdmin
Then, everything got working fine.
Maybe there is a better way, but this did solve my problem. Hope this can help.

Related

django - No User matches the given query. Page Not found 404: User post list view breaks post detail view

I'm fairly new to django and trying to build a message board app. Everything's been running smoothly up until I tried to add functionality to display a list of posts by author. I got it working but then my post detail view started throwing a 'No User matches the given query. Page Not found 404' error and I can't seem to figure out why. Can anyone help?
views.py
class UserPostList(generic.ListView):
model = Post
# queryset = Post.objects.filter(status=1).order_by('-created_on')
template_name = 'user_posts.html'
paginate_by = 6
def get_queryset(self):
"""
Method to return posts restricted to 'published' status AND to
authorship by the user whose username is the parameter in the
url.
"""
user = get_object_or_404(User, username=self.kwargs.get('username'))
return Post.objects.filter(
status=1, author=user
).order_by('-created_on')
class FullPost(View):
def get(self, request, slug, *args, **kwargs):
"""
Method to get post object.
"""
queryset = Post.objects.filter(status=1)
post = get_object_or_404(queryset, slug=slug)
comments = post.comments.order_by('created_on')
liked = False
if post.likes.filter(id=self.request.user.id).exists():
liked = True
return render(
request,
"full_post.html",
{
"post": post,
"comments": comments,
"liked": liked
},
)
# I'll be adding a comment form in here too
urls.py
urlpatterns = [
...
path('<slug:slug>/', views.FullPost.as_view(), name='boards_post'),
...
path('<str:username>/', views.UserPostList.as_view(), name='user_posts'),
...
]
Error message
(When trying to view a single post (previously working) after adding the UserPostList view and route)
Using the URLconf defined in mhcmsgboard.urls, Django tried these URL patterns, in this order:
1. admin/
2. summernote/
3. register/ [name='register']
4. profile/ [name='profile']
5. login/ [name='login']
6. logout/ [name='logout']
7. new/ [name='create_post']
8. <slug:slug>/update/ [name='update_post']
9. <slug:slug>/delete/ [name='delete_post']
10. <str:username>/ [name='user_posts']
The current path, test-post/, matched the last one.
for <str:name> in path "-" not allowed to use in name,
when you use Slug both paths are equal.
urlpatterns = [
...
path('<slug:slug>/', views.FullPost.as_view(), name='boards_post'),
...
path('<slug:username>/', views.UserPostList.as_view(), name='user_posts'),
]
there are 2 simple ways
use sub path for one or both paths : user_post/<slug:username>/
Use re_path and define the regex to change path
exp:
re_path(r'^(?P\<username>\w+)/$', views.UserPostList.as_view()),
The problem is that you match update_post, delete_post and user_posts URL's to the root. As the error message explains, the current request is made against user_posts. And it seems that you don't have a user called test-post.
You could solve it e.g. with the following URL conf:
urlpatterns = [
...
path('board/<slug:slug>/', views.FullPost.as_view(), name='boards_post'),
...
path('posts/<str:username>/', views.UserPostList.as_view(), name='user_posts'),
...
]
That way, each path is unique and Django knows which View it has to call.

Django REST framework RetrieveAPIView gets empty "id" parameter and returns 404 error

I use Django 1.11 + Django REST Framework. I'm trying to get detailed data about distinct record using generic RetrieveAPIView from Django REST Framework and server returns "HTTP 404 Not Found" result, i.e. no data.
models.py file:
class TestPage( models.Model):
title = models.CharField( size = 120)
counter = models.IntegerField( default = 0)
def __unicode__(self):
return u'' + self.title
serializers.py file:
class TestPageSerializer(serializers.ModelSerializer):
class Meta:
fields = ('id', 'title', 'counter',)
model = models.TestPage
urls.py file:
urlpatterns = [
url( r'^admin/', admin.site.urls),
url( r'^api/', include( 'rest_framework.urls')),
url( r'^api/', include( 'testpages.urls')),
]
testpages/urls.py file:
urlpatterns = [
url( r'^$', views.TestPageList.as_view()),
url( r'^(?P<id>)\d+/$', views.TestPageDetail.as_view()),
]
and at last views.py file:
class TestPageList(generics.ListAPIView):
lookup_field = 'id'
queryset = TestPage.objects.all()
serializer_class = TestPageSerializer
class TestPageDetail(generics.RetrieveAPIView):
lookup_field = 'id'
queryset = TestPage.objects.all()
serializer_class = TestPageSerializer
# def get_object( self):
# print self.kwargs
If I request "http://127.0.0.1:8000/api/" for all records in the list, server returns all records. But if I want to get record by id using "http://127.0.0.1:8000/api/1/" for example, I get the empty result like in the photo below.
[![enter image description here][1]][1]
If I uncomment get_object()-function in last 2 lines, I can see {'id': u''} in terminal i.e. server gets empty parameter 'id'.
What wrong with this code?
The issue is your regular expression matching for id, you're putting \d+ outside of the matching group when it's actually what you want to match:
url(r'^(?P<id>\d+)/$', views.TestPageDetail.as_view())
By the way, to be good REST citizen, you shouldn't add the / at the end of a URL for a specific object.
Note: As mentioned by JPG, adding a name for the resource you're fetching (plural) would be proper RESTful:
url(r'^pages/(?P<id>\d+)$', views.TestPageDetail.as_view())
This way, you fetch page nr. 1 at /api/pages/1
The original problem was the closing parathesis (the regex expression), you were misplaced that.
urlpatterns = [
url(r'^$', views.TestPageList.as_view()),
url(r'^(?P<id>\d+)/$', views.TestPageDetail.as_view()),
^^^ here
]
Apart from that, You should change the urls patterns to a sensible form, as
#testpages/urls.py
urlpatterns = [
url(r'^test/$', views.TestPageList.as_view()),
url(r'^test/(?P<id>\d+)/$', views.TestPageDetail.as_view()),
]
then the list-api will be available on /api/test/ endpoint and the detail-api will be available on /api/test/{id}/

flask-admin incorrect relative url

I am trying to create a simple admin for editing a mongo collection. I have posted the code below. It all works perfectly locally or as a docker container. However when I deploy this in our micro-service architecture the app lives at: SERVER_NAME/TEAM_NAME/APP_NAME/.
Flask routes set with #app.route work correctly. However the urls in the admin templates are not correct and always start directly at SERVER_NAME ignoring team-name and app-name. The actual pages and resources are located at the correct urls but the urls for the static resources are not found. How do I make sure the urls generated within flask-admin also take into account the relative url?
The code:
import os
import flask_admin
from wtforms import form, fields
from flask_admin.contrib.pymongo import ModelView, filters
# User admin
class WordPairsForm(form.Form):
text = fields.StringField("Text")
language = fields.SelectField("Language", choices=[("de", "german"), ("en", "english"), ("pl", "polish")])
label = fields.SelectField("Label", choices=[("badword", "bad word"), ("no_stay", "no overnight stay")])
active = fields.BooleanField("Active", default="checked")
class WordPairsView(ModelView):
column_list = ("text", "language", "label", "active")
column_sortable_list = ("text", "language", "label", "active")
column_searchable_list = ("text",)
column_filters = (
filters.FilterLike("text", "Text"),
filters.FilterNotLike("text", "Text"),
filters.FilterEqual("language", "Language", options=[("de", "german"), ("en", "english"), ("pl", "polish")]),
filters.FilterEqual("label", "Label", options=[("badword", "bad word"), ("no_stay", "no overnight stay")]),
filters.BooleanEqualFilter("active", "Active")
)
form = WordPairsForm
def create_form(self):
_form = super(WordPairsView, self).create_form()
return _form
def edit_form(self, obj):
_form = super(WordPairsView, self).edit_form(obj)
return _form
def get_list(self, *args, **kwargs):
count, data = super(WordPairsView, self).get_list(*args, **kwargs)
return count, data
def get_url
def add_admin(app):
admin = flask_admin.Admin(
app,
name="CQAS Admin",
url=os.getenv(
"F_ADMIN_URL",
"/admin"
),
static_url_path=os.getenv("F_ADMIN_STATIC_URL", None),
subdomain=os.getenv("F_ADMIN_SUBDOMAIN", None),
endpoint=os.getenv("F_ADMIN_ENDPOINT", None)
)
admin.add_view(WordPairsView(app.data.data, "WordPairs"))

Django Haystack custom SearchView for pretty urls

I'm trying to setup Django Haystack to search based on some pretty urls. Here is my urlpatterns.
urlpatterns += patterns('',
url(r'^search/$', SearchView(),
name='search_all',
),
url(r'^search/(?P<category>\w+)/$', CategorySearchView(
form_class=SearchForm,
),
name='search_category',
),
)
My custom SearchView class looks like this:
class CategorySearchView(SearchView):
def __name__(self):
return "CategorySearchView"
def __call__(self, request, category):
self.category = category
return super(CategorySearchView, self).__call__(request)
def build_form(self, form_kwargs=None):
data = None
kwargs = {
'load_all': self.load_all,
}
if form_kwargs:
kwargs.update(form_kwargs)
if len(self.request.GET):
data = self.request.GET
kwargs['searchqueryset'] = SearchQuerySet().models(self.category)
return self.form_class(data, **kwargs)
I keep getting this error running the Django dev web server if I try and visit /search/Vendor/q=Microsoft
UserWarning: The model u'Vendor' is not registered for search.
warnings.warn('The model %r is not registered for search.' % model)
And this on my page
The model being added to the query must derive from Model.
If I visit /search/q=Microsoft, it works fine. Is there another way to accomplish this?
Thanks for any pointers
-Jay
There are a couple of things going on here. In your __call__ method you're assigning a category based on a string in the URL. In this error:
UserWarning: The model u'Vendor' is not registered for search
Note the unicode string. If you got an error like The model <class 'mymodel.Model'> is not registered for search then you'd know that you haven't properly created an index for that model. However this is a string, not a model! The models method on the SearchQuerySet class requires a class instance, not a string.
The first thing you could do is use that string to look up a model by content type. This is probably not a good idea! Even if you don't have models indexed which you'd like to keep away from prying eyes, you could at least generate some unnecessary errors.
Better to use a lookup in your view to route the query to the correct model index, using conditionals or perhaps a dictionary. In your __call__ method:
self.category = category.lower()
And if you have several models:
my_querysets = {
'model1': SearchQuerySet().models(Model1),
'model2': SearchQuerySet().models(Model2),
'model3': SearchQuerySet().models(Model3),
}
# Default queryset then searches everything
kwargs['searchqueryset'] = my_querysets.get(self.category, SearchQuerySet())

custom html field in the django admin changelist_view

I'd like to some little customisation with the django admin -- particularly the changelist_view
class FeatureAdmin(admin.ModelAdmin):
list_display = (
'content_object_change_url',
'content_object',
'content_type',
'active',
'ordering',
'is_published',
)
list_editable = (
'active',
'ordering',
)
list_display_links = (
'content_object_change_url',
)
admin.site.register(get_model('features', 'feature'), FeatureAdmin)
The idea is that the 'content_object_change_url' could be a link to another object's change_view... a convenience for the admin user to quickly navigate directly to the item.
The other case I'd have for this kind of thing is adding links to external sources, or thumbnails of image fields.
I had thought I'd heard of a 'insert html' option -- but maybe I'm getting ahead of myself.
Thank you for your help!
You can provide a custom method on the FeatureAdmin class which returns HTML for content_object_change_url:
class FeatureAdmin(admin.ModelAdmin):
[...]
def content_object_change_url(self, obj):
return 'Click to change' % obj.get_absolute_url()
content_object_change_url.allow_tags=True
See the documentation.
Pay attention and use format_html (See docs here) as the mark_safe util has been deprecated since version 1.10. Moreover, support for the allow_tags attribute on ModelAdmin methods will be removed since version 1.11.
from django.utils.html import format_html
from django.contrib import admin
class FeatureAdmin(admin.ModelAdmin):
list_display = (
'change_url',
[...]
)
def change_url(self, obj):
return format_html('<a target="_blank" href="{}">Change</a>', obj.get_absolute_url())
change_url.short_description='URL'
It took me two hours to find out why Daniel Roseman's solution doesn't work for me. Even though he is right, there's one exception: when you want to make custom calculated fields (read only) in the Admin. This wont work. The very easy solution (but hard to find) is to return your string in a special constructor: SafeText(). Maybe this is linked with Django 2 or with readonly_fields (which behave differently from classical fields)
Here's a working sample that works but doesn't without SafeText():
from django.utils.safestring import SafeText
class ModelAdminWithData(admin.ModelAdmin):
def decrypt_bin_as_json(self, obj):
if not obj:
return _("Mode insert, nothing to display")
if not obj.data:
return _("No data in the game yet")
total = '<br/><pre>{}</pre>'.format(
json.dumps(json.loads(obj.data),
indent=4).replace(' ', ' '))
return SafeText(total) # !! working solution !! <------------------
decrypt_bin_as_json.short_description = _("Data")
decrypt_bin_as_json.allow_tags = True
readonly_fields = ('decrypt_bin_as_json',)
fieldsets = (
(_('Data dump'), {
'classes': ('collapse',),
'fields': ('decrypt_bin_as_json',)
}),
)