Making a Web Page That Displays A DocType's Data

I am new to Frappe and web development in general and am trying to carry out the simple task of creating a web page that displays a DocType’s data (in this case, a list of customers, each of whom has a first name, last name, address, and a certain order he or she has placed).

As somebody who has experience in programming but is new to web development, I have found the information that I have read thus far to be rather vague and scattershot; so far, I see what tools and components may be involved, but I have yet to figure out how in the hell they all work together (and which ones, exactly, I should use).

If I’m right, these include using the REST API, using generators, using jquery, writing client-side or server-side Python and JavaScript scripts, etc.–things that I need to thoroughly figure out before taking the next step. I’ve noticed that the Frappe framework presents an overall open-ended approach to solving problems, which is why I’m so lost.

As somebody mostly familiar with Python, I am trying to find a way to do this by using it (although, of course, I will likely have to use a little bit of everything else).

This is only the beginning of a big learning experience, and I appreciate any help!

Thanks!

5 Likes

Did u gone through
https://frappe.io/docs

@vongol
Please try this Generators

2 Likes

Yes, I have already read that a few times and failed to achieve my goal. I still don’t understand how that is supposed to create a web page. I really don’t. These instructions don’t explain each step enough, leaving out information regarding the frappe module’s objects, functions, variables, etc. Questions include:

  1. What am I supposed to write in the templates/includes/breadcrumbs.html file? Do I have to make this file?
  2. What does a context object do? Is it necessary? Does it aid in displaying the data on the page?

And now, having edited the DocType’s Python script and made an HTML file in the DocType’s templates/generators folder, I’m getting this error:

Error: cannot import name WebSiteGenerator

Once again, I can’t find the info I need (in this case, info regarding this error). Here’s the whole traceback…

Traceback (most recent call last): File "/home/jimmy/frappe-bench/apps/frappe/frappe/app.py", line 56, in application response = frappe.handler.handle() File "/home/jimmy/frappe-bench/apps/frappe/frappe/handler.py", line 21, in handle data = execute_cmd(cmd) File "/home/jimmy/frappe-bench/apps/frappe/frappe/handler.py", line 52, in execute_cmd return frappe.call(method, **frappe.form_dict) File "/home/jimmy/frappe-bench/apps/frappe/frappe/__init__.py", line 922, in call return fn(*args, **newargs) File "/home/jimmy/frappe-bench/apps/frappe/frappe/desk/form/save.py", line 22, in savedocs doc.save() File "/home/jimmy/frappe-bench/apps/frappe/frappe/model/document.py", line 230, in save return self._save(*args, **kwargs) File "/home/jimmy/frappe-bench/apps/frappe/frappe/model/document.py", line 280, in _save self.run_post_save_methods() File "/home/jimmy/frappe-bench/apps/frappe/frappe/model/document.py", line 790, in run_post_save_methods self.run_method("on_update") File "/home/jimmy/frappe-bench/apps/frappe/frappe/model/document.py", line 666, in run_method out = Document.hook(fn)(self, *args, **kwargs) File "/home/jimmy/frappe-bench/apps/frappe/frappe/model/document.py", line 887, in composer return composed(self, method, *args, **kwargs) File "/home/jimmy/frappe-bench/apps/frappe/frappe/model/document.py", line 870, in runner add_to_return_value(self, fn(self, *args, **kwargs)) File "/home/jimmy/frappe-bench/apps/frappe/frappe/model/document.py", line 660, in <lambda> fn = lambda self, *args, **kwargs: getattr(self, method)(*args, **kwargs) File "/home/jimmy/frappe-bench/apps/frappe/frappe/core/doctype/doctype/doctype.py", line 220, in on_update self.run_module_method("on_doctype_update") File "/home/jimmy/frappe-bench/apps/frappe/frappe/core/doctype/doctype/doctype.py", line 273, in run_module_method module = load_doctype_module(self.name, self.module) File "/home/jimmy/frappe-bench/apps/frappe/frappe/modules/utils.py", line 184, in load_doctype_module raise ImportError('Module import failed for {0} ({1})'.format(doctype, module_name + ' Error: ' + str(e))) ImportError: Module import failed for Customers (customers.customers.doctype.customers.customers Error: cannot import name WebSiteGenerator)

Hi @vongol, and welcome to the community!

First, thanks for the well-written post. I know exactly what you mean. And how you feel. I started working with Frappe and ERPNext back in January. I haven’t been able to spend as much time as I like (I still have my day job). But very slowly…I’ve been piecing things together.

You are not alone. Several of us feel that learning Frappe is a struggle. As you’ve seen, our knee-jerk response is to just point at online documentation.

  • Does online documentation exist?
    • Yes. Certainly. To an extent.
  • Where is it?
    • All over the place. I probably have links to a dozen different places I’ve found.
  • Will it help you?
    • Maybe. Sometimes. But it’s an incomplete picture. You have to piece it together, and figure it out through trial and error.

The problem is that our documentation is scattered. Sometimes outdated. And very much incomplete. Learning from scratch (especially for programmers like you and me, who have not previously done this kind of web development) is quite difficult.

At first, this really bothered me. I struggled with it, and got frustrated. Eventually I decided to just start from scratch, and document as-I-go. When I learn new things, I write them down. But it’s very much trial-and-error.

What time zone are you in? One thing I’ve been thinking about is getting all of us “newbies” together online. And having online training classes, maybe 1 hour at a time. Ideally with someone to guide us. But worst case, just teach ourselves.

By coincidence, I recently started learning Hapi.js. I needed it for other projects. Anyhow, having learned that, I find it’s been very enlightening in terms of ERPNext. By learning more about Node and Hapi.js, I have a better grasp of asynchronous callbacks, routes, template rendering, etc. Probably common knowledge to programmers who have previously done web. But for a non-web programmer like myself, that was a real eye opener for me.

6 Likes

Hi @brian_pond,
We definitely together solve some documentation issues with frappe. I’ve never seen such a good community and library but first few steps definitely painful. We can make it joyful together.

Note: I am also trying to show a doctype data on public page for a while.

@TurkerTunali
@brian_pond

Great to hear that I’m not alone and that you guys strive to learn and teach each other!

Well, here’s the good news: I finally figured out how to do it. I have realized that I was overthinking everything that i was doing, questioning the control flow of the framework and every little component that it has. I’ve been upholding a “do-first-ask-questions-later” sort of policy.

It is, indeed, a long, sometimes tedious process of trial and error; just as some like to compare coding to writing or construction work, I like to compare it to building a puzzle.

The solution was very simple: in my case, in order to display a table of customers and their respective orders, I made an HTML file in the www folder along with a corresponding Python file. Then, in the Python file, I just imported the frappe module and used the get_context(context) function to assign an instance variable of context to a list (or dictionary, I think) containing DocType objects. By using this context function, you initialize the variable that would serve as the pool of the data you want to display.

(Note: for some reason, the frappe.get_all([DocType], fields=[fields], etc.) function, which returns a list (or dictionary—l can’t remember) of all objects that are of a certain DocType, does not work at the moment, and I have made a post about this newfound, pesky problem; so, for the time being, I decided to take a crude approach by making a list of objects that are of the “Order” DocType by calling the frappe.get_doc([DocType], [name]) function ad nauseum, which is intended to retrieve data from a single object.

import frappe

def get_context(context):
    context.orders = [frappe.get_doc("Order", "2995f8477d"),
                      frappe.get_doc("Order", "170fe55ed2"),
                      frappe.get_doc("Order", "98eb82155c"),
                      frappe.get_doc("Order", "39e10adb32"),
                      frappe.get_doc("Order", "a0ecfce3de")]

''' Currently defunct '''
def get_orders():
    return frappe.get_all("Order",
                          fields=["first_name", "last_name", "address", "order"],
                          order_by="last_name desc")

Then, in the HTML file, I made a fancy table that lists data from the documents via a simple loop:

<h2> Orders </h2>

{% if orders %}

  <head>
    <style>
      table {
        font-family: arial, sans-serif;
        border-collapse: collapse;
        width: 100%;
      }

      td, th {
        border: 1px solid #dddddd;
        text-align: left;
        padding: 8px;
      }

      tr:nth-child(even) {
        background-color: #dddddd;
      }
    </style>
  </head>

  <table>
      <tr>
        <th>Name</th>
        <th>Address</th>
        <th>Items</th>
        <th>Total</th>
      </tr>
    {% for order in orders %}
      <tr>
        <td>{{ order.first_name }} {{ order.last_name }}</td>
        <td>{{ order.address }}</td>
        <td>
          <ul style="list-style-type:disc">
              {% for i in order.order %}

                <li>{{  frappe.get_doc('Product', i.item).name1 }}</li>

              {% endfor %}
          </ul>
        </td>
        <td> </td>
      </tr>
    {% endfor %}
  </table>

{% else %}

  <p style="font-size:36px; ">No orders!</p>

{% endif %}

And I got this!

Screenshot_20170817_153849|690x388

4 Likes

@vongol Great to hear that. At least you have working list. I will try website generators and if I fail I will try your way by creating appropirate files on www folder.
Thanks.

@TurkerTunali

You’re welcome!

Whether you use generators depends on the design of your program. For instance, I didn’t use them because I chose to list all objects of a certain DocType on one page rather than create an unnecessary page for every single DocType.

@vongol
I have a single doctype and I want to show them on public. So I guess creating files on www folder is one of proper ways with frappe. Right?

So you have one DocType and several instances of it?

Do you want each instance to have its own page or do you want to show all the data on one page?

I don’t need to list them. I want to show each doctype data on custom link something like example.com/doctype-name/123 where 123 is name of document.

I have one doctype and several instances of it. I want to show all data of one instance on a single page. I don’t need a list page.

For web development,
We use simple ajax & jquery.

e.g
Onload of page call js function

in js,fetch data using ajax call

In python fetch data using sql and make html using render template

Finally
append data to html div using jquery.

We made this page.

5 Likes

This is something like a complete tutorial. Thank you @kolate_sambhaji.

we dont have access to server side so i want to implement this in javascript how can i implement this in client side js??

doing same but not getting expected output