I added a number of records to a Django db table (machina forums) directly via a script (ie: I did not use the site admin interface). The structure seemed fairly straightforward with no foreign keys in other tables.
However the resulting displays are uneven. In a forum index display all of the children forums display under a category. However if I go into the category, only forums added via the admin interface are visible. There does not appear to be any difference in the db records between those that were added programmatically and those added via the admin interface.
I am guessing the issue has to do with indexes on the table. However when I use a GUI to view the db all of the indexes show "result set is empty."
Any ideas about what is causing the problem and if it is index related, how do I update the index?
Here is the view that creates the forum displays:
Forum views
===========
This module defines views provided by the ``forum`` application.
"""
from django.http import HttpResponseRedirect
from django.shortcuts import get_object_or_404
from django.views.generic import ListView
from machina.apps.forum.signals import forum_viewed
from machina.conf import settings as machina_settings
from machina.core.db.models import get_model
from machina.core.loading import get_class
Forum = get_model('forum', 'Forum')
Topic = get_model('forum_conversation', 'Topic')
ForumVisibilityContentTree = get_class('forum.visibility', 'ForumVisibilityContentTree')
PermissionRequiredMixin = get_class('forum_permission.viewmixins', 'PermissionRequiredMixin')
TrackingHandler = get_class('forum_tracking.handler', 'TrackingHandler')
class IndexView(ListView):
""" Displays the top-level forums. """
context_object_name = 'forums'
template_name = 'forum/index.html'
def get_queryset(self):
""" Returns the list of items for this view. """
return ForumVisibilityContentTree.from_forums(
self.request.forum_permission_handler.forum_list_filter(
Forum.objects.all(), self.request.user,
),
)
def get_context_data(self, **kwargs):
""" Returns the context data to provide to the template. """
context = super(IndexView, self).get_context_data(**kwargs)
visiblity_content_tree = context['forums']
# Computes some global values.
context['total_posts_count'] = sum(n.posts_count for n in visiblity_content_tree.top_nodes)
context['total_topics_count'] = sum(
n.topics_count for n in visiblity_content_tree.top_nodes
)
return context
class ForumView(PermissionRequiredMixin, ListView):
""" Displays a forum and its topics. If applicable, its sub-forums can also be displayed. """
context_object_name = 'topics'
paginate_by = machina_settings.FORUM_TOPICS_NUMBER_PER_PAGE
permission_required = ['can_read_forum', ]
template_name = 'forum/forum_detail.html'
view_signal = forum_viewed
def get(self, request, **kwargs):
""" Handles GET requests. """
forum = self.get_forum()
if forum.is_link:
response = HttpResponseRedirect(forum.link)
else:
response = super(ForumView, self).get(request, **kwargs)
self.send_signal(request, response, forum)
return response
def get_forum(self):
""" Returns the forum to consider. """
if not hasattr(self, 'forum'):
self.forum = get_object_or_404(Forum, pk=self.kwargs['pk'])
return self.forum
def get_queryset(self):
""" Returns the list of items for this view. """
self.forum = self.get_forum()
qs = (
self.forum.topics
.exclude(type=Topic.TOPIC_ANNOUNCE)
.exclude(approved=False)
.select_related('poster', 'last_post', 'last_post__poster')
)
return qs
def get_controlled_object(self):
""" Returns the controlled object. """
return self.get_forum()
def get_context_data(self, **kwargs):
""" Returns the context data to provide to the template. """
context = super(ForumView, self).get_context_data(**kwargs)
# Insert the considered forum into the context
context['forum'] = self.get_forum()
# Get the list of forums that have the current forum as parent
context['sub_forums'] = ForumVisibilityContentTree.from_forums(
self.request.forum_permission_handler.forum_list_filter(
context['forum'].get_descendants(), self.request.user,
),
)
# The announces will be displayed on each page of the forum
context['announces'] = list(
self.get_forum()
.topics.select_related('poster', 'last_post', 'last_post__poster')
.filter(type=Topic.TOPIC_ANNOUNCE)
)
# Determines the topics that have not been read by the current user
context['unread_topics'] = TrackingHandler(self.request).get_unread_topics(
list(context[self.context_object_name]) + context['announces'], self.request.user,
)
return context
def send_signal(self, request, response, forum):
""" Sends the signal associated with the view. """
self.view_signal.send(
sender=self, forum=forum, user=request.user, request=request, response=response,
)
Seems like you're talking about some custom solution and it is hard to help without additional details like SQL queries that you've applied, model code, queries, and their returns.
Related
I have a Model with a lot of entries, so I'm using django-filters to filter the model, I initially load an empty table and from there I use the filter to view the items.
Everything works fine, the page loads initially with no entry, after I filter, django shows the correct items.
The Url gets a parameter: /retetabloc/?acordcadru=532(532 is the filter) but when I try to update an entry, the filter resets(the parameter is still in the URL) and the whole db is loaded.
I don't quite understand how to pass the filter parameter to the RetetaBlocUpdate, so that after the update is done it returns to the filtered items like in the ListView.
views.py
class RetetaBlocListview(LoginRequiredMixin, CoreListView):
model = RetetaBloc
def get_queryset(self, *args, **kwargs):
pdb.set_trace()
acordcadru = self.request.GET.get("acordcadru")
queryset = RetetaBloc.objects.filter(acordcadru=acordcadru)
return queryset
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['filter'] = RetetaBlocFilter(self.request.GET, queryset=self.get_queryset())
pdb.set_trace()
return context
class RetetaBlocUpdate(LoginRequiredMixin, AjaxUpdateView):
model = RetetaBloc
form_class = RetetaBlocForm
Thank you.
If you'd like filters to be remembered you could add them to a session variable instead. That way filters would be recalled even if they didn't go back directly from the update page (and you'd wouldn't have redundant URL querystring on pages where they weren't needed).
Something like:
def get_queryset(self, *args, **kwargs):
pdb.set_trace()
#check for new filter in URL first
acordcadru = self.request.GET.get("acordcadru")
#if nothing check for session variable
if not acordcadru:
acordcadru = self.request.session.get('acordcadru')
#if something in URL querystring, set it in session variable
else:
self.request.session['acordcadru'] = acordcadru
queryset = RetetaBloc.objects.filter(acordcadru=acordcadru)
return queryset
I am trying to capture the POST request data for each field and store it in the session so that I can use the data in another view. But I am getting errors in the other view because the session variables are returning 'None'.
Before, I had written the code using a non-class based view and fetched the values with request.POST.get('name') and this worked. What should I be doing here?
class TripsView(FormView):
""" A view to show all trips and receive trip search data """
template_name = "products/trips.html"
form_class = SearchTripsForm
def form_valid(self, form):
"""
Takes the POST data from the SearchTripsForm and stores it in the session
"""
trip_choice = form.cleaned_data["destination"].id
self.request.session["destination_choice"] = trip_choice
self.request.session["searched_date"] = "26-12-2021"
self.request.session["passenger_total"] = form.cleaned_data[
"passengers"
]
return super(TripsView, self).form_valid(form)
def get_context_data(self, **kwargs):
""" Adds to the context the Product objects categorized as trips """
context = super().get_context_data(**kwargs)
context["destinations"] = Product.objects.filter(category=3)
return context
def get_success_url(self):
""" Overides the success url when the view is run """
return reverse("selection")
My other view is as follows:
class SelectTripView(View):
"""
Provides the user a set of choice options based on their search input in
products.TripsView
"""
template_name = "bookings/trips_available.html"
form_class = DateChoiceForm
def get(self, request):
"""
Initialises the DateChoiceForm with data from SearchTripsForm
& render to the template
"""
searched_date = self.request.session["searched_date"]
print(searched_date)
naive_searched_date = datetime.strptime(searched_date, "%Y-%m-%d")
gte_dates = self.trips_matched_or_post_date(searched_date)
lt_dates = self.trips_preceding_date(searched_date)
... etc etc...
The code flags an error here at the datetime.strptime() method because it says argument 1 must be a string not a tuple - (None)
Update:* I have located that the problem is because the date string is being converted to a tuple (2,0,2,1 etc..) - maybe because it includes punctuation dashes '-' between Y-M-D? Is there a workaround for this? That being said, the string was just a placeholder. In the real case, I am pulling the value from form data which gives me date object and would need to be serialized before sending to the session.
I am confused as to why using request.POST.get() to retrieve the form data in non classed based View did not encounter such errors. Is there a difference to the data once it is cleaned_data?
hi im trying to save a form data into db.
i provided print of requset.data for you as you see requirement have two items.
i want to save each item in database i used for loop to save each item of list but the loop will save each character of item like h-e-l,... in table row...
where is my mistake ... thanks
also print of request.data.get('requirement') will retun second item
this is print of request.data in sever:
<QueryDict: {'requirement': ['hello', 'bye'], 'audience': ['adasd'], 'achievement': ['asdasd'], 'section': ['410101010'], 'title': ['asdasd'], 'mini_description': ['asdad'], 'full_description': ['asdasd'], 'video_length': ['10101'], 'video_level': ['P'], 'price': [''], 'free': ['true'], 'image': [<InMemoryUploadedFile: p.gif (image/gif)>]}>
view:
class StoreCreateAPIView(generics.CreateAPIView):
parser_classes = (MultiPartParser, FormParser)
permission_classes = [IsAuthenticated]
def perform_create(self, serializer):
serializer.save(author=self.request.user)
def post(self, request, *args, **kwargs):
if request.method == 'POST':
print(request.data)
file_serial = ProductSerializer(data=request.data, context={"request": request})
if file_serial.is_valid():
file_serial.save(author_id=request.user.id)
requirement = request.data['requirement']
audience = request.data.get('audience')
achievement = request.data.get('achievement')
sections = request.data.get('section')
print(request.data['requirement'])
pid = file_serial.data.get('product_id')
for item in requirement :
req = ProductRequiredItems(
item = item,
product_id = pid
)
req.save()
First of all, overriding CreateAPIView's post method in your code makes your custom perform_create method useless, unless you explicitly call it from within your customized post method. Otherwise it will never be called.
also print of request.data.get('requirement') will retun second item
It does return the last item as per Django docs for QueryDict.__getitem__(key).
i want to save each item in database i used for loop to save each item of list but the loop will save each character of item like h-e-l,...
This is because of the above functionality of QueryDict. When you do:
requirement = request.data['requirement']
# requirement = request.__getitem__('requirement')
it will call QueryDict.__getitem__(key) method and thus return only the last item (which is string in you example).
Answer:
You can simply override CreateAPIView's create method, and let your serializer handle all the rest.
# views.py
from django.shortcuts import render
from rest_framework import generics, status
from rest_framework.response import Response
from .models import MyObj
from .serializers import MyObjSerializer
class MyObjView(generics.CreateAPIView):
serializer_class = MyObjSerializer
queryset = MyObj.objects.all()
def create(self, request, *args, **kwargs):
# The QueryDicts at request.POST and request.GET will be immutable
# when accessed in a normal request/response cycle.
# To get a mutable version you need to use QueryDict.copy().
req_data = request.data.copy()
requirements = req_data.pop('requirement')
serializers_data = []
for requirement in requirements:
req_data ['requirement'] = requirement
serializer = self.get_serializer(data=req_data)
serializer.is_valid(raise_exception=True)
self.perform_create(serializer)
serializers_data.append(serializer.data)
return Response(serializers_data, status=status.HTTP_201_CREATED)
# serializers.py
from rest_framework import serializers
from .models import MyObj
class MyObjSerializer(serializers.ModelSerializer):
class Meta:
model = MyObj
fields = '__all__'
Have a look at DRF CreateModelMixin. It defines create & perform_create methods that are used used in CreateAPIView upon executing POST request. I just altered them slightly to handle your specific case.
Hope it helps.
I have been combing through the internet for quite some while without finding any solution to this problem.
What I am trying to do...
I have the following models:
class TrackingEventType(models.Model):
required_previous_event = models.ForeignKey(TrackingEventType)
class TrackingEvent(models.Model):
tracking = models.ForeignKey(Tracking)
class Tracking(models.Model):
last_event = models.ForeignKey(TrackingEvent)
Now the main model is Tracking, so my admin for Tracking looks like this:
class TrackingEventInline(admin.TabularInline):
model = TrackingEvent
extra = 0
class TrackingAdmin(admin.ModelAdmin):
inlines = [TrackingEventInline]
That's it for the current setup.
Now my quest:
In the TrackingAdmin, when I add new TrackingEvent inlines, I want to limit the options of TrackingEventType to onlye those, that are allowed to follow on the last TrackingEvent of the Tracking. (Tracking.last_event == TrackingEventType.required_previous_event).
For this, I would need to be able to access the related Tracking on the InlineTrackingEvent, to access the last_event and filter the options for TrackingEventType accordingly.
So I found this: Accessing parent model instance from modelform of admin inline, but when I set up TrackingEventInline accordingly:
class MyFormSet(forms.BaseInlineFormSet):
def _construct_form(self, i, **kwargs):
kwargs['parent_object'] = self.instance
print self.instance
return super(MyFormSet, self)._construct_form(i, **kwargs)
class MyForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
print kwargs
self.parent_object = kwargs.pop('parent_object')
super(MyForm, self).__init__(*args, **kwargs)
class TrackingEventInline(admin.TabularInline):
form = MyForm
formset = MyFormSet
model = TrackingEvent
extra = 0
I get a KeyError at /admin/.../tracking/2/change/ 'parent_object' from self.parent_object = kwargs.pop('parent_object')
Does anyone know how to solve this? Am I approaching the problem the wrong way? I guess this would be pretty easy in a custom form in the frontend, but I really want to use the admin, because the whole application is built to be used from the admin, and it would be a hell lot of work to build a custom admin interface just because of this problem :)
Ok, so posting on StackOverflow is always helping to get the problem straight. I was able to put together a solution that works for me.
It includes defining my own Form in a outer function, as well as defining two InlineAdmin objects for TrackingEvent (one for update / edit, one just for insert).
Here's the code:
def create_trackingevent_form(tracking):
"""
"""
class TrackingEventForm(forms.ModelForm):
"""
Form for Tracking Event Inline
"""
def clean(self):
"""
May not be needed anymore, since event type choices are limited when creating new event.
"""
next_eventtype = self.cleaned_data['event_type']
tracking = self.cleaned_data['tracking']
# get last event, this also ensures last_event gets updated everytime the change form for TrackingEvent is loaded
last_eventtype = tracking.set_last_event()
if last_eventtype:
last_eventtype = last_eventtype.event_type
pk = self.instance.pk
insert = pk == None
# check if the event is updated or newly created
if insert:
if next_eventtype.required_previous_event == last_eventtype:
pass
else:
raise forms.ValidationError('"{}" requires "{}" as last event, "{}" found. Possible next events: {}'.format(
next_eventtype,
next_eventtype.required_previous_event,
last_eventtype,
'"%s" ' % ', '.join(map(str, [x.name for x in tracking.next_tracking_eventtype_options()]))
)
)
else:
pass
return self.cleaned_data
def __init__(self, *args, **kwargs):
# You can use the outer function's 'tracking' here
self.parent_object = tracking
super(TrackingEventForm, self).__init__(*args, **kwargs)
self.fields['event_type'].queryset = tracking.next_tracking_eventtype_options()
#self.fields['event_type'].limit_choices_to = tracking.next_tracking_eventtype_options()
return TrackingEventForm
class TrackingEventInline(admin.TabularInline):
#form = MyForm
#formset = MyFormSet
model = TrackingEvent
extra = 0
#readonly_fields = ['datetime', 'event_type', 'note']
def has_add_permission(self, request):
return False
class AddTrackingEventInline(admin.TabularInline):
model = TrackingEvent
extra = 0
def has_change_permission(self, request, obj=None):
return False
def queryset(self, request):
return super(AddTrackingEventInline, self).queryset(request).none()
def get_formset(self, request, obj=None, **kwargs):
if obj:
self.form = create_trackingevent_form(obj)
return super(AddTrackingEventInline, self).get_formset(request, obj, **kwargs)
I hope this helps other people with the same problem.. Some credit to the Stack Overflow threads that helped me come up with this:
Prepopulating inlines based on the parent model in the Django Admin
Limit foreign key choices in select in an inline form in admin
https://docs.djangoproject.com/en/1.9/ref/models/instances/#django.db.models.Model.clean_fields
Please do not hesitate to ask questions if you have any
I have a form which looks like this:
class AddressSearchForm(forms.Form):
"""
A form that allows a user to enter an address to be geocoded
"""
address = forms.CharField()
I'm not storing this value, however, I am geocoding the address and checking to make sure it is valid:
def clean_address(self):
address = self.cleaned_data["address"]
return geocode_address(address, True)
The geocode function looks like this:
def geocode_address(address, return_text = False):
""" returns GeoDjango Point object for given address
if return_text is true, it'll return a dictionary: {text, coord}
otherwise it returns {coord}
"""
g = geocoders.Google()
try:
#TODO: not really replace, geocode should use unicode strings
address = address.encode('ascii', 'replace')
text, (lat,lon) = g.geocode(address)
point = Point(lon,lat)
except (GQueryError):
raise forms.ValidationError('Please enter a valid address')
except (GeocoderResultError, GBadKeyError, GTooManyQueriesError):
raise forms.ValidationError('There was an error geocoding your address. Please try again')
except:
raise forms.ValidationError('An unknown error occured. Please try again')
if return_text:
address = {'text':text, 'coord':point}
else:
address = {'coord':point}
return address
What I now need to do is create a view that will query a model using the address data to filter the results. I'm having trouble figuring out how to do this. I'd like to use CBV's if possible. I can use FormView to display the form, and ListView to display the query results, but how do I pass the form data between the two?
Thanks in advance.
UPDATE: I know how to query my model to filter the results. I just don't know how to properly combine using a Form and Class Based Views so that I can access the cleaned_data for my filter. e.g:
Process should be:
1) Display form on get
2) Submit form and validate (geocode address) on post
3) Run query and display results
address = form.cleaned_data['address']
point = address['coord']
qs = model.objects.filter(point__distance_lte=(point, distance)
Ok, here's a generic version of what ended up working based on psjinx direction:
from django.views.generic.base import TemplateResponseMixin, View
from django.views.generic.edit import FormMixin
from django.views.generic.list import MultipleObjectMixin
class SearchView(FormMixin, MultipleObjectMixin, TemplateResponseMixin, View):
"""
A View which takes a queryset and filters it via a validated form submission
"""
queryset = {{ initial queryset }} # you can use a model here too eg model=foo
form_class = {{ form }}
def get(self, request, *args, **kwargs):
form_class = self.get_form_class()
form = self.get_form(form_class)
return self.render_to_response(self.get_context_data(form=form))
def post(self, request, *args, **kwargs):
form_class = self.get_form_class()
form = self.get_form(form_class)
if form.is_valid():
return self.form_valid(form)
else:
return self.form_invalid(form)
def form_valid(self, form):
queryset = self.get_queryset()
search_param = form.cleaned_data['{{ form field }}']
object_list = queryset.filter({{ filter operation }}=search_param)
context = self.get_context_data(object_list=object_list, form=form, search_param=search_param)
return self.render_to_response(context)
This is similar to a question asked here or I will say a combination of two questions.
Django: Search form in Class Based ListView
Search multiple fields of django model without 3rd party app
django simple approach to multi-field search (if your models similar to one mentioned in this question)
Please have a look at above questions and their answers and if you still have any question then reply in comment to this answer.
Update 1
from django.views.generic.base import TemplateResponseMixin, View
from django.views.generic.edit import FormMixin
from django.views.generic.list import MultipleObjectMixin
class SearchView(FormMixin, MultipleObjectMixin, TemplateResponseMixin, View):
model = SomeModel
form_class = AddressSearchForm
template = "search.html"
def get_queryset():
## Override it here
pass
def post():
## If your form is invalid then request won't reach here
## because FormMixin is in left most position
## do something
## call self.render_to_response()
def form_valid():
## Your form is valid do something
## if form is invalid then next method will be called
pass
def form_invalid(self):
## form is not valid
## render to some template
Useful Links:
https://github.com/django/django/blob/1.4.3/django/views/generic/base.py
https://github.com/django/django/blob/1.4.3/django/views/generic/edit.py
https://github.com/django/django/blob/1.4.3/django/views/generic/list.py
Related Question:
Django - Mixing ListView and CreateView