ACF Post Object in Timber not showing on page template - advanced-custom-fields

Data isn't showing on page template using ACF Post Object and calling it with Timber.
Tried to add the data to index.php and the news.php template.
ACF Post Object Field Name:
news_author_data
news.php
$post = new TimberPost();
$context['post'] = $post;
$context['news_author'] = new
TimberPost(get_field('news_author_data'));
Timber::render('core/news.twig', $context);
news.twig
{% for news in news_author %}
test
{% endfor %}
No error message. Just no data.

You're going about it the wrong way, support for ACF is baked into Timber, as per the docs here:
[https://timber.github.io/docs/guides/acf-cookbook/]
Assuming this is a repeater field all you need to do is add this to your news.twig file
<div class="my-list">
{% for item in post.meta('news_author_data') %}
<div class="item">
<h4>{{ item.my_repeater_field }}</h4>
<h6>{{ item.my_repeater_field }}</h6>
<img src="{{ Image(item.picture).src }}" />
</div>
{% endfor %}
</div>

If get_field('news_author_data') returns an array of post objects you can't put the collection directly into the "new TimberPost" constructor. You have to loop through them like so:
$post = new TimberPost();
$context['post'] = $post;
$context['news_author'] = array_map(function($post) {
return new TimberPost($post);
}, get_field('news_author_data'));
Timber::render('core/news.twig', $context);
Hope this helps.

Related

Calling a view as a value for action's attribute in HTML forms that leads to a NoReverseMatch error

I am creating a web application using django, and I want now to add a view that modifies entries (my web application is an encyclopedia, so the view let us editing the page).
We may be able to access the editing page by cliking on the link of this html page:
{% extends "encyclopedia/layout.html" %}
{% block title %}
{{ title }}
{% endblock %}
{% block body %}
{{ html | safe }}
{% if exists %}
<br><br><br><br>
Edit encyclopedia
{% endif %}
{% endblock %}
So django'll go through this url
urlpatterns = [
...
...
...
path("<str:title>/edit", views.edit, name="edit"),
]
Then, this url should bring us to a this view:
def edit(request, title):
if request.method == "POST":
form = NewForm(request.POST)
if form.is_valid():
with open(f"entries/{title}.md", "w") as file:
file.write(form.cleaned_data["content"])
return redirect(reverse("encyclopedia:display"))
else:
return render(request, "encyclopedia/edit.html",{
'form' : NewForm(),
'message' : """<div class="alert alert-danger" role="alert">
Your entries are empty.
</div>"""
})
markup = util.get_entry(title)[0]
print(request.)
return render(request, "encyclopedia/edit.html",{
'form' : NewForm(),
'title' : title,
'markup': markup,
})
And here is my html file:
{% extends "encyclopedia/layout.html" %}
{% block title %}
Edit
{% endblock %}
{% block body %}
<h1>New Encyclopedia</h1>
<p>Our websites' encyclopedias are written in a langage names Markdow<br>
You may have additionnal informations about this language here.</p>
<form action="{% url 'encyclopedia:edit' %}" method="POST" style="display: block; text-align: center; padding: 20px;">
{% csrf_token %}
<p style="margin: 15px;">{{ title }}</p>
<textarea rows='10' placeholder="Encyclopedia's content" name="content" style="width: 90%; margin: 15px;">{{ markup }}</textarea>
<input type="submit" value="Edit" style="width:15%">
</form>
{% endblock %}
But my problem is that when I run my application, and go to the editing page, I get a NoReverseMatch error just like this one:
NoReverseMatch at /wiki/Django/edit
Reverse for 'edit' with no arguments not found. 1 pattern(s) tried: ['wiki/(?P[^/]+)/edit$']
I think that this problem is linked to the fact that I don't give the title argument when I call the editing view in my form, but I don't know how to do this.
If anyone could help me that would just be amazing, I made many researches, but couldn't really understand how to fix the problem...
I've finally found the origin of the error, to whom it may help, it was that I forgott to add the 'title' variable (that is obligatory for my view) just like this:
else:
return render(request, "encyclopedia/edit.html",{
'form' : NewForm(),
'message': """<div class="alert alert-danger" role="alert">
Your entries are empty.
</div>""",
'title' : title,
})
The is_valid() method doesn't really corresponds to how do I want to treat data, I've replaced it with this function:
undecodables = ["è", "à", "é"]
def own_validation(string):
try:
valid = False
for i in string:
if i.isalnum : valid = True
assert i not in undecodables
except AssertionError:
return False
else:
return valid
I don't know what are all the undecodable characters, so the list'll be completed in the future.
Thanks to #iklinac for your help !

Django multiple images for post to render

For the past few days I have been trying to give access to the admin user to upload multiple images/slides for every single post, one idea I had in mind was nesting a for loop inside the posts for loop that for every post, render also every image associated with it but it seem's I cant get it quite right.
class Post(models.Model):
title = models.CharField(max_length = 128)
image = models.ImageField(default = 'default.jpg', upload_to = 'post_pics')
content = models.TextField()
date_posted = models.DateTimeField(default = timezone.now)
category = models.ForeignKey(Category, on_delete = models.CASCADE)
def __str__(self):
return f"{self.title} - {self.category}"
def get_image_filename(instance, filename):
title = instance.post.title
slug = slugify(title)
return "post_images/%s-%s" % (slug, filename)
class Images(models.Model):
post = models.ForeignKey(Post, default= None, on_delete = models.CASCADE, related_name= 'Images')
image = models.ImageField( upload_to = get_image_filename, verbose_name = 'Images')
def __str__(self):
return f"imazh per {self.post}"
and my Html:
<div class="post-list-container">
{% for post in posts %}
<article class="post-container">
<div class="post-top">
> Focus on the for loop here
{% for post.Images in post.Images %}
<img src="{{ post.Images.url }}">
{% endfor %}
<div class="post-title"><h1>{{ post.title }} </h1></div>
<div class="post-images">
<img class="rounded" src="{{ post.image.url }}">
</div>
</div>
<div class="post-middle">
<div class="post-content"><p> {{ post.content }}</p> </div>
</div>
<div class="post-bottom">
<div class="post-category"><h2>{{ post.category }}</h2>
</div>
<div class="post-date_posted"><h1>{{ post.date_posted|date:"F d, Y" }}</h1>
</div>
</div>
</article>
{% endfor %}
</div>
Is there any way to render those images this way?
A Post will have a related set, which is what you refer to that reverse relationship as.
By default, django will make the relationship on a Post instance images_set because it takes your model name on the relationship & adds _set.
You can also choose your own related name by setting the related_name attribute on the FK field. (docs)
Here's an example;
class Map(models.Model):
members = models.ManyToManyField(
User,
related_name='maps',
verbose_name=_('members')
)
# Reverse relationship:
User.maps.all()
Or in python using your models;
post = Post.objects.first()
print(f"Images in post {post.title}")
for image in post.images_set.all():
print(image.url)
So without a custom related_name, your template loop would be something like;
{% for image in post.images_set.all %}
<img src="{{ image.url }}">
{% empty %}
<p>No images found</p>
{% endfor %}

How to access field names of javascript object in HTML passed in from backend?

Example:
var obj={name:bob,
}
I want to access not the value of name i.e. bob instead an array containing keys of obj like [name].
<h1>{{ pagename|title }}</h1>
<ul>
{% for author in collections %}
<li >
{{ author.uno }}
{{ author.subject }}
<script>
var array = Object.keys(author).map((key) => key) ;
document.write('<p>' + array + '</p>');
</script>
{% for element in author %}
<li >
{{element }}
</li>
{% endfor %}
</li>
{% endfor %}
</ul>
Here collections is an array of objects passed in from backend i.e. nodejs.
Author is a javascript object.
I have tried getting desired result using logic inside script tag.
But it is not printing anything on webpage.
I have also tried placing {{}} at different positions without getting fruitful results.
update: I forgot you're using swig-template. Here is my suggestion:
//backend :
const author = { /* author object */ };
author.keys = Object.keys(author).join(', ');
swig.renderFile('/path/to/template.html',{ author });
and then, put it in template.htm;
{{ author.subject }}
{{ author.keys }}
{% for element in author %}
<li >
{{element }}
</li>
{% endfor %}

how to extract images for every single post in django

I have two different models. One is for posts and other one is for images.
what I want is to display all images for every single post. Here is the file for models:
class Cars_Posts(models.Model):
user = models.ForeignKey(MyUser, on_delete=models.CASCADE)
post_city = models.ForeignKey(City, on_delete=models.CASCADE)
post_title = models.CharField(max_length=256)
post_detail = models.TextField(max_length=65536)
price = models.PositiveIntegerField()
def __str__(self):
return "%s %s %s %s %s"(
self.user.id, self.post_city,
self.post_title, self.post_detail, self.price,)
class Images_Cars(models.Model):
post = models.ForeignKey(Cars_Posts, on_delete=models.CASCADE)
car_images = models.ImageField(upload_to='car_images', blank=True, null=True )
def __str__(self):
return "%s %s " % (self.post_id, self.car_images, )
Here is the view FUNCTION FOR SEARCH (QUERY DATA BASE):
def search(request):
template = 'path/to/template.html'
# get all cities in data base
all_p_cities = City.objects.all()
#get the exact city selected by user and passed through the variable h_qc
query_c = request.GET.get('h_qc')
# get posts under the selected city
result = Cars__Posts.objects.filter(Q(post_city__city_name__iexact=query_c)
# get posts IDs of the result object
p_id=[]
for item in result:
p_id+= [item.id]
# print(p_id)
#get all images under each single post
all_images=[]
for item in p_id:
images = Images_Cars.objects.filter(post_id = item)
all_images+=[images]
# print (p_id)
# print(result)
print(all_images)
context = {
'post_city' : query_c,
'result': result,
'all_images':all_images,
}
return render(request, template, context )
both files run with no error. However in the template I want to display all posts and all images for every single post. I need your help in here. I looped over results and could get all posts. But I got also all images for every single posts repeated in every loop. So , instead of getting only images related to the current post, I got all images for all posts in current post and all subsequent posts. Any help or suggestion will be appreciated. Also any suggestion for writing the code in view file with an efficient and sophisticated way is appreciated.
Here is portion of the HTML File:
{% csrf_token %}
{% for item in result %}
<li class="result-row">
<!-- image box -->
<span>
<a href="#" class="result-image-act" >
{% for image in all_images %}
<!-- if image list is empty display the default image -->
{% if not image %}
<img src="{% static 'path/to/default image' %}" class="active">
{% endif %}
<!-- I have class ="active" which is supposed to be class of first image -->
{% if image and image.0 %}
<img class="active" src="{{image.item.car_images.url}}" >
{% elif image and not image.0 %}
<img src="{{image.item.car_images.url}}" >
{% endif %}
{% endfor %}
</a>
<span class="embed-result-price">{{item.price}}</span>
<div class="swipe-wrap">
<div class="swipe-wrap-lef">
<span class="move" >
<div class="swipe-prev">
<p><</p>
</div>
</span>
</div>
<div class="swipe-wrap-rig">
<span class="move" >
<div class="swipe-next">
<p>></p>
</div>
</span>
</div>
</div>
</span>
<span class="result-price">{{result.price}}
</span>
<span class="result-price">{{result.title}}</span>
</li>
{% endfor %}
Note: inside the html javascript code that will take care of swipe images for every single post that is why I fetch for the first image for every single post to add class="active" to it
You can do it directly in templates with reverse related name:
add a property method in your ImageCars model to check whether files are actually exist
class Images_Cars(models.Model):
''' code '''
car_images = models.ImageField(upload_to='car_images', blank=True, null=True )
#property
def car_images_exists(self):
return self.car_images.storage.exists(self.car_images.name)
templates
{% for item in result %}
{% for image in item.images_cars_set.all %}
#<!-- The images_cars instance is here -->
{% if image.car_images_exists %} # see changes in models
{{image.car_images.url}}
{% endif %}
{% empty %}
#<!--Item has no image_cars instances here: item.images_cars_set.all -->
<h3>No Images</h3>
{% endfor %}
{% endfor %}
You don't really need to loop through the queryset in views and again in templates
result as template variable is enough to have everything in templates

Get data from data folder?

I need to get some data using a dynamic name from front matter:
{{ site.data.prd-[page.tag].title" }}
The above fails to get the string from the data folder and nothing is output.
Where am I going wrong?
I am not sure about what you are trying to do but how about make a dictionary type data in _data directory and search for a title that matches page.tag in desired pages?
_data.prd-tags.yml
For each item, we can access the key and value using index ([0]: key, [1]: value).
jekyll: "jekyll's title"
ruby: "ruby's title"
html: "html's title"
.
.
.
HTML fragment for searching and displaying a tag (if found)
Iterate over site.data.prd-tags and display title if an item that matches page.tag is found.
<div class="tag-title">
{% for tag in site.data.prd-tags %}
{% if tag[0] == page.tag %}
{{ tag[1] }}
{% else %}
Nothing found
{% endif %}
{% endfor %}
</div>