despite of many mordern website employ an OSS for serving image, I still want to build a backend to manage small thumbnails locally.
however, django image field is a bit tricky.
there are three views I may change image reference:
models.py
views.py
forms.py
I used to do it simply by:
forms.py
request.user.profile.image = self.files['image']
and I always have a default
models.py
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
image = ProcessedImageField(
default='profile/default.jpg',
upload_to='profile',
processors=[ResizeToFill(*THUMBNAIL_SIZE)],
format='JPEG',
options={'quality': THUMBNAIL_QUALITY},
)
After a lot of testing, I found that it always result in an issue, can be among:
default image file get deleted.
if the image has been set before, it holds an value that is not the default, when I reset it, the old referenced file is not deleted and will occupy disk storage.
to do it perfectly, I decided to write a global function for import, whenever I want to set an image, call it
from django.conf import settings
def setImage(instance, attr, file):
""" instance will be saved """
if file:
ifield = getattr(instance, attr)
# old reference, can be default
iurl = ifield.url
# default
durl = settings.MEDIA_URL + instance._meta.get_field(attr).get_default()
if iurl != durl:
# old reference is not default
# delete old reference and free up space
ifield.delete(save=True)
# set new file
setattr(ifield, attr, file)
instance.save()
pretty straight-forward. however, in testing, I found the image will never be set. Following are the possibale reasons I have eliminated:
form multipart enctype attribute
ajax processData, contentType is correctly set
save in model class is not overriden
if it's all ok, where went wrong? I logged out all the values.
setImage(self.user.profile, 'image', self.files['image'])
# self.files['image'] has valid value and is passed
# to setImage, which I think, is not garbage collected
def setImage(instance, attr, file):
""" instance will be saved """
print('======')
print(file)
if file:
ifield = getattr(instance, attr)
iurl = ifield.url
durl = settings.MEDIA_URL + instance._meta.get_field(attr).get_default()
print(iurl)
print(durl)
if iurl != durl:
ifield.delete(save=True)
print(f'--- del {iurl}')
setattr(ifield, attr, file)
print('----res')
print(getattr(ifield, attr))
print(ifield.image)
print('--- ins')
print(instance)
instance.save()
print('--- after save')
print(instance.image.url)
print(getattr(instance, attr))
the field has a default value, and I upload the screen shot in testing.
======
Screen Shot 2022-11-03 at 10.59.41 pm.png
/media/profile/default.jpg
/media/profile/default.jpg
----res
Screen Shot 2022-11-03 at 10.59.41 pm.png
Screen Shot 2022-11-03 at 10.59.41 pm.png
--- ins
tracey
--- after save
/media/profile/default.jpg
profile/default.jpg
why the image is not setting, anybody have any ideas?
Solutions
after tons of testing, only a line went wrong and just simple a wrong variable.
def setImage(instance, attr, file):
""" instance will be saved """
if file:
ifield = getattr(instance, attr)
iurl = ifield.url
durl = settings.MEDIA_URL + instance._meta.get_field(attr).get_default()
if iurl != durl:
ifield.delete(save=True)
setattr(instance, attr, file)
instance.save()
for form update validation, here's a method I've worked out and testified working:
def clean_image(form, attr:str, field:str):
"""
form.instance.attr -> ImageFile
form.cleaned_data[field] -> ImageFile
"""
upload_img = form.cleaned_data[field]
if form.instance:
# condition: update
# create does not matter
ifield = getattr(form.instance, attr)
iurl = ifield.url
if isinstance(upload_img, InMemoryUploadedFile):
# upload new file
# else, the file is not changed
durl = settings.MEDIA_URL + form.instance._meta.get_field(attr).get_default()
if iurl != durl:
ifield.delete(save=True)
return upload_img
e.g. you can call it simply like:
class Project(models.Model):
image = ProcessedImageField(
default='projects/default.jpg',
upload_to='projects',
processors=[ResizeToFill(*THUMBNAIL_SIZE)],
options={'quality': THUMBNAIL_QUALITY},
format='JPEG',
)
class ProjectCreateForm(forms.ModelForm):
class Meta:
model = Project
fields = ['name', 'image']
def clean_image(self):
return clean_image(self, 'image', 'image')
Honestly, I did not test your function to say what is wrong with it. Instead I implemented it in my own way taking your model as a base. If you really want a set_image(instance, attr, file) function, you can adapt it from this answer create_profile_ajax at views.py.
settings.py
DEFAULT_IMAGE_URL = 'profile/default.jpg'
models.py
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
image = ProcessedImageField(
default=settings.DEFAULT_IMAGE_URL,
upload_to='avatars',
processors=[ResizeToFill(100, 50)],
format='JPEG',
options={'quality': 60},
blank=True,
null=True
)
#staticmethod
def default_image_absolute_url():
return settings.MEDIA_URL + settings.DEFAULT_IMAGE_URL
#staticmethod
def default_image_url():
return settings.DEFAULT_IMAGE_URL
forms.py
class ProfileForm(forms.ModelForm):
class Meta:
model = Profile
fields = ['image']
views.py
#login_required
def create_profile(request):
form = ProfileForm()
return render(request, 'profile/create.html', {'form': form})
def create_profile_ajax(request):
image = request.FILES.get('image')
profile, created = Profile.objects.get_or_create(user=request.user)
if image:
if profile.image.url == Profile.default_image_absolute_url():
profile.image = image
else:
profile.image.delete()
profile.image = image
else:
profile.image = Profile.default_image_url()
profile.save()
profile.refresh_from_db()
return JsonResponse({'new_image_url': profile.image.url})
profile/create.html (csrf with ajax)
{% extends 'base.html' %}
{% block content %}
{{form.as_p}}
<input type="submit" value="Create" onclick="sendProfile()">
<img src="{{request.user.profile.image.url}}"
id="thumb"
width="500"
height="600"
{% if not request.user.profile.image %} hidden {% endif %}
style="object-fit: contain;">
<script>
function getCookie(name) {
...
}
function sendProfile() {
const csrftoken = getCookie('csrftoken');
var input = document.getElementById('id_image');
var data = new FormData()
data.append('image', input.files[0])
fetch('/your/ajax/url/', {
method: 'POST',
headers: {
'X-CSRFToken': csrftoken
},
body: data
})
.then((response) => response.json())
.then((data) => {
var thumb = document.getElementById('thumb');
thumb.src = data.new_image_url;
thumb.hidden = false;
input.value = '';
});
}
</script>
{% endblock %}
quotting FormData documentation:
When using FormData to submit POST requests using XMLHttpRequest or
the Fetch_API with the multipart/form-data Content-Type (e.g. when
uploading Files and Blobs to the server), do not explicitly set the
Content-Type header on the request.
Related
My purpose is to create a django form to select devices filtered by country and club fields.
My form is this:
class MyForm(Form):
country = ChoiceField(choices=some_choices, initial=None)
club = CharField(widget=Select())
expiration_date = DateField()
sales_info = ChoiceField(choices=SALES_TYPES, initial=None)
devices = MultipleChoiceField(widget=CheckboxSelectMultiple(choices=[]))
def clean_devices(self):
devices = self.cleaned_data.get("devices")
if not devices:
raise ValidationError("At least one device must be selected.")
return devices
def save(self):
...
views.py
class MyFormView(LoginRequiredMixin, FormView):
template_name = "store/my_template.html"
form_class = MyFormView
def get_success_url(self):
return reverse('init_device')
def form_valid(self, form):
init_device = form.save()
if init_device:
return super().form_valid(form)
return super().form_invalid(form)
def form_invalid(self, form):
logger.error(form.errors)
my_template.html
<form action="{% url 'init_device' %}" method="post">
{% csrf_token %}
...
<select id="id_devices" name="devices" multiple>
I populate select field via javascript in this way:
let device_select = document.getElementById("id_devices");
serialIds.forEach(serialId => {
let option = document.createElement("option");
option.text = serialId;
option.value = serialId;
device_select.add(option);
});
I obtained filtered devices form db using a websocket by now I can't pass them to the form because this error raises:
ValueError: The view path.view didn't return an HttpResponse object. It returned None instead.
Then I print form.errors and this is showed <ul class="errorlist"><li>devices<ul class="errorlist"><li>Select a valid choice. device_attriubte is not one of the available choices.</li></ul></li></ul>
Can anyone help me?
Too much at once?
First get the user's choice of country and club and validate them as far as possible. When done, redirect to an URL such as
app/choose_device/country/club
and in the view which gets invoked, use kwargs country and club in the queryset of its form's ModelMultipleChoiceField. This normalizes to a list of model instances (device objects) when the form validates.
(I'm assuming that devices are objects in your DB. THis is implied by your use of "filtered" but isn't explicit).
Im working on a social media application in django and would like to rename all the images from the uploaded content to make it easier to reuse them (putting them in to a pdf is the end goal, right now the filenames are the same as uploaded and i don't know how put these paths into the pdf --> solution might be numbering them all).
The filename should be renamed to: postimg{num_post}
all posts are numbered. the specific number or the post should be the end of the filename of the image file.
models.py
def post_images(instance, filename):
ext = filename.split('.')[-1]
filename = "%s_%s.%s" % (instance.post.num_post, ext)
return os.path.join('uploads', filename)
class Post(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4)
num_post = models.IntegerField(default=0)
image = models.ImageField(upload_to='post_images')
caption = models.TextField(max_length=300)
created_at = models.DateTimeField(auto_now_add=True)
number_of_likes = models.IntegerField(default=0)
number_of_dislikes = models.IntegerField(default=0)
def __str__(self):
return self.caption
views.py
def upload(request):
if request.method == 'POST':
#user = request.user.username
image = request.FILES.get('image_upload')
#--> how to rename the image file to "post{num_post}.jpg"
caption = request.POST['caption']
num_post = Post.objects.count()+1
new_post = Post.objects.create(image=image, caption=caption, num_post=num_post)
new_post.save()
#create pdf
buffer = io.BytesIO()
#get the image
#img_file = Image.open(f'{os.getcwd()}/{post.image.url}').convert('RGB')
#img_file = f'media/post_images/postimg{num_post}'
#x_start = 0
#y_start = 0
#saving it on the server
folder_path = f"media/post{num_post}.pdf"
folder_name = os.path.basename(folder_path)
p = canvas.Canvas(folder_name)
#p.drawImage(img_file, x_start, y_start, width=120, preserveAspectRatio=True, mask='auto')
p.drawString(200, 300, new_post.caption)
p.drawString(200, 100, str(new_post.created_at))
p.drawString(200, 600, str(new_post.id))
#p.drawText(new_post.caption)
#p.drawImage(new_post.image)
p.showPage()
p.save()
buffer.seek(0)
return redirect('/'), folder_path
else:
return redirect('/')
so in the end i should be able to put the image in the pdf by using:
img_file = f'media/post_images/postimg{num_post}'
x_start = 0
y_start = 0
p.drawImage(img_file, x_start, y_start, width=120, preserveAspectRatio=True, mask='auto')
I was already able to get the images into the pdf by using the existing filename but since the pdf should be automatically generated for each post, the image name needs to be variable, i think.
Right now, it is not working. The image is not renamed, but there is also no error display. so the function seems not to reach the image? How do I make it work?
Thank you for any suggestions. :) im new to django... any explanation helps.
Lets start with the model field:
class Post(models.Model):
...
image = models.ImageField(upload_to='post_images')
...
It is not showing any errors because you are uploading files to 'post_images' folder and not calling a function with such name. And that is also why your files are not being renamed.
To call the rename function, just remove the single quote:
image = models.ImageField(upload_to=post_image)
Although, it is not going to work because of this line:
def post_images(instance, filename):
...
filename = "%s_%s.%s" % (instance.post.num_post, ext)
...
Where there are two problems. First, instance is a post object instance, you are trying to access it in the wrong way. And second when trying to format three strings with two parameters. That being said:
def post_images(instance, filename):
ext = filename.split('.')[-1]
filename = 'post{}.{}'.format(instance.num_post, ext)
return os.path.join('uploads', filename)
Lets also implement a #property on model Post so we can access its image filename with ease:
class Post(models.Model):
...
#property
def filename(self):
return os.path.basename(self.image.name).split('.')[0]
Now, related to the view, it is always a good practice to work with django forms (model form), it also helps writing less code while doing more (like validating data):
forms.py
from django import forms
from .models import Post
class UploadForm(forms.ModelForm):
class Meta:
model = Post
fields = ['image', 'caption']
views.py
from django.shortcuts import render, redirect
from .forms import UploadForm
from .models import Post
import os
from reportlab.pdfgen import canvas
from reportlab.lib.pagesizes import A4
def upload(request):
if request.method == 'POST':
form = UploadForm(request.POST, request.FILES)
if form.is_valid():
form.cleaned_data['num_post'] = Post.objects.count() + 1
post = Post.objects.create(**form.cleaned_data)
p = canvas.Canvas(os.getcwd() + f'/uploads/converted/{post.filename}.pdf')
p.drawString(200, 300, post.caption)
p.drawString(200, 100, str(post.created_at))
p.drawString(200, 600, str(post.id))
p.showPage()
p.drawImage(post.image.name, 0,0, width=A4[0], height=A4[1],mask='auto')
p.showPage()
p.save()
return redirect('post:upload')
else:
form = UploadForm()
return render(request, 'upload.html', {'form': form})
In this view, after checking that the data is valid we then update the valid data dictionary and create the object. Furthermore, since we are using .create() it is not necessary to save the object afterwards.
After this first process we create ReportLab PDF object, with two pages, the first one containing the string related data (as in your original function). And, the second one where we draw an image fitting the whole page by using from reportlab.lib.pagesizes import A4 size properties. You can find all this information at the userguide documentation.
upload.html
{% block content %}
<form action="{% url 'post:upload' %}" enctype="multipart/form-data" method="post">
{% csrf_token %}
{{form.as_p}}
<button type="submit">Create</button>
</form>
{% endblock content %}
urls.py
app_name = 'post'
urlpatterns = [
path('upload/', views.upload, name='upload'),
]
I am using django-bootstrap3 to render my forms on the template and i have been struggling to find what is causing the error Parameter "field" should contain a valid Django BoundField when i try to load the page with the form on it. I have attached my code and error below. can someone please point for me what i'm doing wrong?
forms.py
class OrderForm(forms.Form):
first_name = forms.CharField(max_length=50)
last_name = forms.CharField(max_length=50)
email = forms. EmailField(max_length=50)
institution_name = forms.CharField(max_length=150)
phone = forms.IntegerField()
address = forms.CharField(max_length=100)
city = forms.CharField(max_length=50)
item = forms.CharField(max_length=100)
serial_number = forms.CharField(max_length=50)
problem = forms.CharField(widget=forms.Textarea(attrs—Crows':10,'cols':18,'style':'resize:none', [placeholder':'Please define your problem here'l),label='Problem description')
[placeholder':'Please define your problem here'l),label='Problem description')
views.py
def Orderview(request):
if request.method == 'Post':
order_form = OrderForm(request.POST)
if order_form.is_valid():
cd = order form.cleaned data
subject = '{} repair order from {}'.format(cd['item'],cd['institution_name'])
from_email = cd['email']
to = [settings.EMAIL_HOST_USER,]
ctx = {
'first_name':cd['first_name'],
'last_name':cd['last_name'],
'email':cd['email'],
'institution_name':cd['institution_name'],
'phone':cd['phone'],
'address':cd['address'],
'city':cd['city'],
'item':cd['item'],
'serial_number.:cd['serial_number'],
'problem':cd['problem'],
}
message = get_template('electroapp/email/order.html').render(Context(ctx))
msg = EmailMessage(subject,message,to=to,from_email=from_email)
msg.content_subtype='html'
msg.send()
messages.success(request,' Your Repair order has been sent',)
return redirect('electroapp:repair_order')
else:
order_form = OrderForm()
return render(request,'electroapp/orderform.html',{'Order_form':order_form})
template
browser error
console logs
This could be because some fields may be missing.Take a look at this
You could do something like this to see what fields are available:
<form role="form" method="post">
{% csrf_token %}
{% bootstrap_form order_form %}
{% buttons submit='OK' reset="Cancel" %}{% endbuttons %}
</form>
and then try to figure out why you are having missing fields.
Parameter "field" should contain a valid Django BoundField
Why can't they just tell us which actual field? Well, here's how I solved it.
A little debugging and you'll find this is thrown within renderers.py:
class FieldRenderer(BaseRenderer):
"""Default field renderer."""
# These widgets will not be wrapped in a form-control class
WIDGETS_NO_FORM_CONTROL = (CheckboxInput, RadioSelect, CheckboxSelectMultiple, FileInput)
def __init__(self, field, *args, **kwargs):
if not isinstance(field, BoundField):
raise BootstrapError('Parameter "field" should contain a valid Django BoundField.')
self.field = field
super().__init__(*args, **kwargs) ...
The message is not helpful, but can be fixed in your dev/test environment.
WARNING: The following will void your warranty! :)
Go up a few steps in the render chain in library.py (for example ~/.virtualenvs/your_env/lib/python3.8/site/packages/django/template/library.py)
and find SimpleNode.
class SimpleNode(TagHelperNode):
def __init__(self, func, takes_context, args, kwargs, target_var):
super().__init__(func, takes_context, args, kwargs)
self.target_var = target_var
def render(self, context):
resolved_args, resolved_kwargs = self.get_resolved_arguments(context)
output = self.func(*resolved_args, **resolved_kwargs)
if self.target_var is not None:
context[self.target_var] = output
return ''
if context.autoescape:
output = conditional_escape(output)
return output
After copying the original file to a backup, change it as follows. We are wrapping the line
output = self.func(*resolved_args, **resolved_kwargs)
with a try/except. Here's the result:
class SimpleNode(TagHelperNode):
def __init__(self, func, takes_context, args, kwargs, target_var):
super().__init__(func, takes_context, args, kwargs)
self.target_var = target_var
def render(self, context):
resolved_args, resolved_kwargs = self.get_resolved_arguments(context)
###
from bootstrap3.exceptions import BootstrapError
try:
output = self.func(*resolved_args, **resolved_kwargs)
except BootstrapError as b:
raise BootstrapError(str(b).replace('"field"', self.args[0].token)) from b
###
if self.target_var is not None:
context[self.target_var] = output
return ''
if context.autoescape:
output = conditional_escape(output)
return output
Within the SimpleNode class we have access to the name of the field that couldn't be rendered. So we intercept the exception there and re-throw it with the useful tidbit injected. Voila, you will now get an exception report that tells you the actual problem field.
Cheers.
I'm new to django and want to make a form that allows a user to select one of three images using radio boxes, once the user selects the image it is saved to their profile and displayed on their profile page.
I am using:
django 1.8.3
userena 1.4.1
Any help or links to documentation that will help would be great.
Basic example. Models:
def get_upload(instance, filename):
return "uploaded_files/user_%s_%s_%s" % (instance.user, datetime.datetime.today().strftime("%d-%m-%Y %H-%M-%S"), filename)
class UserModel():
# your fields
image = models.FileField(upload_to=get_upload, default='')
FileField
Forms:
class UploadForm(forms.ModelForm):
"""Auxiliary class to make file uploading more convenient."""
def __init__(self, *args, **kwargs):
super(UploadForm, self).__init__(*args, **kwargs)
class Meta:
model = UserModel
fields = ('image')
View:
def upload(request):
if request.method == "POST":
profile = request.user.profile
image_type = request.POST.get("image_type", None)
if image_type == "blah":
profile.image = request.FILES[image_type]
else:
return HttpResponse("Error")
request.user.profile.save()
return HttpResponse('OK')
request.FILES
JS with soem Jquery:
var SIZE_RESTRICT = 10*1024*1024; //10 Mbytes
$(document).ready(function()
{
$(".upload_file").find(".upload_button").click(send_file);
$(".upload_file").find(".upload_file_form").find("input[type='file']").click(enable_upload);
});
function send_file()
{
if ($(this).attr("enabled") != "true") return;
// Prevent double-triple clicks with multiple upload.
$(this).attr("enabled", "false");
var form = $(this).parent().find(".upload_file_form").get(0);
var formData = new FormData(form);
var file = $(form).find("input[type='file']").get(0).files[0];
// Not sure about this
// Prevent attack with HUGE files, that will smash server memory
// TODO: Restrict file types (Ex. MIME-image, pdf, doc)
if (file.size > SIZE_RESTRICT)
{
alert("File is too big.");
return;
}
formData.append("proof_type", $(this).attr("upload-type"));
var xhr = new XMLHttpRequest();
var that = this;
// TODO: Define another events like error, abort
xhr.upload.onprogress = function(e) {
// TODO: Progressbar as e.loaded / e.total
if (e.lengthComputable)
console.log((e.loaded / e.total) * 100);
else
console.log("Cant count length");
};
xhr.onload = function(e){
// TODO: Show success confirmation to user.
if (this.response == "Success")
{
// pass
alert(this.response);
}
else if (this.response == "Error")
{
// pass
alert(this.response);
}
else
{
// pass
}
};
xhr.open('POST', '/upload_proof');
xhr.send(formData);
}
function enable_upload()
{
$(this).parent().parent().find(".upload_button").attr("enabled", "true");
}
Actually another simple example could be founded in docs
If the set of images is small and fixed, the best option is to use the choice attribute in the field that defines the image within your model.
The image field could be the path to the image file on the file system.
class UserProfile(models.Model):
GOOD = 'Good.jpg'
UGLY = 'Ugly.jpg'
BAD = 'Bad.jpg'
AVATAR_CHOICES = (
(GOOD, 'Good'),
(UGLY, 'Ugly'),
(BAD, 'Bad'),
)
avatar_img = models.CharField(max_length=255,
choices=AVATAR_CHOICES,
default=GOOD)
Another option is to use FilePathField as your model field
FilePathField(path="/path/to/avatar_images", match="*.jpg", recursive=False)
Another way is to fill the form field dynamically when the form is instantiated.
Please see this SO QA for more on that.
However, as Django docs say
But if you find yourself hacking choices to be dynamic, you’re
probably better off using a proper database table with a ForeignKey
To specify Radiobuttons to be used for the form field, please refer to the official docs on how to set the proper widget.
I am creating a basic cms/wiki as a learning project. Please forgive my noobish questions.
My wiki has pages. There are different types of pages. They have some fields in common and some unique fields.
I've included three of my models to demonstrate how I've undertakes this. (one model with the common fields that the actual pages inherit from)
class DefaultPage(models.Model):
title = models.CharField(max_length=100)
created_date = models.DateTimeField(auto_now_add=True, auto_now=False)
edited_date = models.DateTimeField(auto_now_add=False,auto_now=True)
child_pages = models.ManyToManyField("self",blank=True)
slug = models.SlugField(max_length=100,editable=False,blank=True)
def __unicode__ (self):
return self.title
def save(self, *args, **kwargs):
if not self.id:
self.slug = slugify(self.title)
super(DefaultPage, self).save(*args, **kwargs)
class Page(DefaultPage):
body = models.TextField(blank=True)
class LearningObject(DefaultPage):
archivefile = models.FileField(upload_to='static/learningobject/archivefiles/%Y/%m/%d')
indexpath = models.CharField(max_length=254,editable=False)
def unpackarchive(self):
archive = self.archivefile
filename = os.path.basename(str(archive))
folder = str(filename).split(".")[0]
print folder
index_found = "False"
with zipfile.ZipFile(archive,"r") as z:
for each in z.namelist():
if each == "index.html" or each == "index.htm":
index_found = "True"
else:
pass
if not index_found:
print "zip file does not contain a valid index.html file"
else:
path = os.path.join("static","learningobject","unpackedarchives",folder)
z.extractall(path)
self.findindex(path)
def findindex(self,path):
print path
for root, dirnames, filenames in os.walk(path):
for filename in fnmatch.filter(filenames, 'index.ht*'):
print filename
self.indexpath = os.path.join(root, filename)
print self.indexpath
def save(self, *args, **kwargs):
self.unpackarchive()
super(LearningObject, self).save(*args, **kwargs)
My urls.py passes the defaultpage.slug to
url(r'^(?P<default_page_slug>[\w\-]+)/$', views.defaultpageview, name='defaultpageview'),
a view that DefaultPage.objects.get(slug=default_page_slug)
def defaultpageview(request, default_page_slug):
context_dict = {}
try:
default_page = DefaultPage.objects.get(slug=default_page_slug)
context_dict['default_page'] = default_page
except:
pass
return render(request,'wiki/default_page.html',context_dict)
which in turn ends up in a template
<!DOCTYPE html>
<html>
<head>
<title>{{default_page.title}}</title>
</head>
<body>
<h1>{{default_page.title}}</h1>
{% if default_page.body %}
<p>
{{default_page.body}}
<p>
{%elif default_page.indexpath %}
{{default_page.indexpath}}
{% endif %}
</body>
</html>
However I am unable to call default_page.indexpath or default_page.body
Am I able to use the method I am attempting or do I need to pass all Models across in the view rather than the parent?
Sorry if the terms I've used are not correct. Please feel free to edit to correct me.
You're quering for DefaultPage objects, so that's what you get. A DefaultPage object doesn't have any of those fields; only the subclasses do. If you want to use body, you'll need to query for the class that contains that field, and the same for indexpath.