MajorLazer
MajorLazer

Reputation: 77

Loop in Dictionary not Working in Python-Flask-HTML

I'm trying to create a dictionary using Python-Flask-HTML that loops through every single character received from the HTML input form and return its corresponding values an an output. For example:

input: Z.123/46X/78 ; output: Y-BCD+EFW+HI

Python-Flask code:

from flask import Flask, render_template, url_for, request, redirect
from flask_sqlalchemy import SQLAlchemy
from datetime import datetime


app = Flask(__name__)


app.config['TEMPLATES_AUTO_RELOAD'] = True

app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///test.db'
db = SQLAlchemy(app)


class mydict(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    content = db.Column(db.String(200), nullable=False)
    date_created = db.Column(db.DateTime, default=datetime.utcnow)

    def __repr__(self):
        return '<Value %r>' % self.id



@app.route('/', methods=['POST','GET'])
def dictionary():

    result = ''

    definition = {'0': 'A', '1': 'B', '2': 'C', '3': 'D', '4': 'E', '5': 'F', '6': 'G', '7': 'H', '8': 'I', '9': 'J',
               'Z': 'Y', 'X': 'W', '/': '+', '.': '-'}


    if request.method == 'POST':
        dict_content = request.form['content']
        for num in dict_content:  
            result += definition[num]  
            new_content = mydict(content=result)

            try:    
                db.session.add(new_content)
                db.session.commit()
                return redirect('/')
            except:
                return 'error '
    else:     
        new_content = mydict.query.order_by(mydict.date_created).all()
        return render_template('index.html', new_content=new_content)



@app.route('/delete/<int:id>')     
def delete(id):
    value_to_delete = mydict.query.get_or_404(id)

    try:
        db.session.delete(value_to_delete)
        db.session.commit()
        return redirect('/')     
    except:
        return 'there was a prob deleting the value'



db.create_all()

if __name__ == "__main__":
    app.run(debug=True)

Dictionary HTML code:

{% extends 'base.html' %}

{% block head %}
<title>Dictionary</title>
{% endblock %}

{% block body %}
<div class="content">

    <h1 style="text-align: center">Dictionary</h1>
    <table>
        <tr>
            <th>Definition</th>
            <th>Added</th>
            <th>Actions</th>
        </tr>
        {% for Value in new_content %}
            <tr>
                <td>{{ Value.content }}</td>
                <td>{{ Value.date_created.date() }}</td>
                <td>
                    <a href="/delete/{{Value.id}}">Delete</a>
                </td>
        {% endfor %}
            </tr>
    </table>


    <div class="form">
    <form action="/" method="POST">
        <input pattern="[A-Z./]+" type="number" name="content" id="content">
        <input type="submit" value="Enter Num">
    </form>
    </div>
</div>
{% endblock %}

Base HTML code:

<html lang="en">
<head>
    <meta charset="UTF-8">
    <link rel="stylesheet" href="{{ url_for('static', filename='css/main.css') }}">
    {% block head %}{% endblock %}
</head>
<body>
    {% block body %}{% endblock %}
</body>
</html>

With the above code, I'm able to enter multiple integers and the output will only be a single letter. If I take out the dictionary portion of the code in the Python app and run it in Pycharm without the rest of the code, then I can get the output that I desire.

Moreover, is there a way to get the HTML input to accept a combination of numbers, letters and special characters? I'm currently using input pattern="[A-Z./]+" type="number" , but this doesn't seem to work. Your help will be greatly appreciated.

Upvotes: 0

Views: 495

Answers (1)

MacOS
MacOS

Reputation: 1159

You have multiple issues in your code.

Input Type

<input pattern="[A-Z./]+" type="number" name="content" id="content">

Here you specify that you only accept a number. This is also the reason why your pattern does not work as you write I'm currently using input pattern="[A-Z./]+" type="number" , but this doesn't seem to work.. But, if I understand you correctly, you want the user to be able to enter a string, i.e. a mix of letters and digits as you specify both of them in your dictionary. You therefore have to chnage this to

<input type="text" name="content" id="content">

This issue is of course linked to your quesiton Moreover, is there a way to get the HTML input to accept a combination of numbers, letters and special characters?. And the answer to this quesiton is yes. But I'm unsure how your pattern should look like. Is this what you are looking for?

<input pattern="[\D|\d][.|\/][\D|\d]{3}[.|\/][\D|\d]{3}[.|\/][\D|\d]{2}" type="text" name="content" id="content">

Redirect in Wrong Place

In addition, you have to move your return statement in the code block where you handle the post request. This is the cause of you single letter returns as the application simply redicrects you to / after it has processed the first element of the input.

    if request.method == 'POST':
        dict_content = request.form['content']
        app.logger.info(dict_content)
        for num in dict_content:
                app.logger.info(num)  
                result += definition[num]  
                new_content = mydict(content=result)

                try:    
                    db.session.add(new_content)
                    db.session.commit()
                except:
                    return 'error '

        return redirect('/')
    else:     
        new_content = mydict.query.order_by(mydict.date_created).all()
        return render_template('index.html', new_content=new_content)

Appearance Issue

However, this leads do another issue with how it is displayed. enter image description here

This is caused by how you handle the saving into you DB. From here on I can't help you anymore because I do not know how you intend this to work.

Code Improvement Suggestion

That said, you can simply do list(my_string) if your input is a simple string. This will return the elements of the string as a list of characters.

my_string = "Z.123/46X/78"
characters = list(my_string)
characters
>> ['Z', '.', '1', '2', '3', '/', '4', '6', 'X', '/', '7', '8']

You can then use your definition dictionary to replace the characters with

characters_exchanged = [definition[character] for character in characters]
characters_exchanged
>> ['Y', '-', 'B', 'C', 'D', '+', 'E', 'G', 'W', '+', 'H', 'I']

Finally, you can merge the list entries back to a string with

my_string_exchanged = ''.join(characters_exchanged)
my_string_exchanged
>> 'Y-BCD+EGW+HI'

Maybe you want to consider rewriting you code that way.

Putting it together

The final function for route / might then be

@app.route('/', methods=['POST','GET'])
def dictionary():

    if request.method == 'POST':

        def exchange_characters(string):
            character_mapping = {
                '0': 'A', '1': 'B', '2': 'C', '3': 'D', '4': 'E', '5': 'F', '6': 'G', '7': 'H', '8': 'I', '9': 'J',
                'Z': 'Y', 'X': 'W', '/': '+', '.': '-'
            }

            character_list = list(string)

            characters_exchanged = [character_mapping[character] for character in character_list]

            return ''.join(characters_exchanged)

        def save_todb(string):
            try:
                string = mydict(content=string)
                db.session.add(string)
                db.session.commit()

                return redirect("/")
            except:
                return 'error '

        string = request.form['content']
        app.logger.info(string)

        exchanged_characters = exchange_characters(string)

        response = save_todb(exchanged_characters) 
        return response
    else:     

        new_content = mydict.query.order_by(mydict.date_created).all()
        return render_template('index.html', new_content=new_content)

HTH

Upvotes: 1

Related Questions