이럴 때 nohup을 사용하면 사용자가 터미널을 종료해도 프로그램이 계속 살아있게 된다.

1.  Nohup
* 정의 : 리눅스, 유닉스에서 쉘스크립트파일(*.sh)을 데몬형태로 실행시키는 프로그램
* Nohup은 리눅스에서 쉘스크립트파일을 데몬형태로 실행시키는 명령어이다.

 - nohup으로 실행을 시키려면 실행파일 권한이 755이상으로 되어있어야 함
 - 명령어 뒤에 '&'를 추가하면 백그라운드로 실행됨 
 - nohup 을 통해 프로그램을 실행시키면 nohup.log 라는 로그 파일 생성
$nohup [실행파일]
$nohup [실행파일] &     // 백그라운드 실행
 

2.  로그 안남기기

$nohup [실행파일] 1>/dev/null 2>&1 &
 
 1. /dev/null  이 표현은 1의 결과를 /dev/null 이라는 파일 속에 넣는다.
    /dev/null로 보내버리면 모든 출력을 없애버린다.
 
 2. &1 이 표현은 2번 파일디스크립터를 1번에 지정된 형식과 동일하게 /dev/null로 지정한다.
     & 은 프로그램을 백그라운드에서 실행하도록 하는 표현이다.
 

3. nohup 종료하기

1. "ps -ef | grep 쉘스크립트파일명"  // 명령으로 데몬형식으로 실행
2. "kill -9 PID번호" // 명령으로 해당 프로세스 종료
 

 



안드로이드 프로그래밍을 하다보면 설정 액티비티를 만들어야 할때가 있습니다.
설정 액티비티들은 리스트로 구성되어있죠.
리스트를 컨버팅하면서 만든다고 해도, 
텍스트만 나오는 row가 있고, 체크박스고 혼재된 row, 입력창이 들어가야하는  row가 있을 수 있습니다.
이럴때 편하게 사용할 수 있는 방법이 있습니다.

바로 PreferenceActivity 를 상속받아서 만드는것이지요.





보통은 아래의 화면처럼 나옵니다.
분류별 항목별로 묶어 분류바를 기준으로 여러 설정들이 보여지죠.
기본적으로는 텍스트, 큰텍스트와 그 밑의 작은 텍스트, 거기에 더해진 체크박스 입니다.

 
 
 
 


사실 직접 이런 화면을 구성해도 되지만, 
특별히 월등한 UI를 제공하고자 하는 목적이 아니라면 쓰라고 있는 기능을 쓰는것이 빠르고 편리합니다.


PreferenceActivity를 사용하기 위해서는 두가지 단계를 거쳐야합니다. 
 1. 레이아웃 작성
 2. 액티비티 작성

사실 일반적인 액티비티들도 모두 레이아웃을 작성하고 액티비티를 작성하기 때문에 그렇게 어렵게 느껴지진 않습니다.
다만, 화면에 어떻게 보여질지, 어떻게 배치할지에 대한 부분을 배제하고 xml레이아웃을 작성할 수 있기때문에 이 방법은 직접 설정 xml레이아웃을 작성하는것보다 빠르고 월등합니다.


 1. 레이아웃 작성

PreferenceActivity에 사용되는 Layout xml은 일반 Activity에 사용하는 xml 과 작성법이 조금 다릅니다.
일반 액티비티에 사용하는 default layout이 LinearLayout 이라면, 
Preference 액티비티에 사용하는 default layout은 PreferenceScreen 입니다.

PreferenceScreen을 기본으로 하는 Layout XML 파일의 작성은 아래와 같이 할 수 있습니다.


PreferenceActivity에 사용할 xml 파일 생성
 
 
 
 


파일을 생성했으면 이제 작성할 차례입니다.

1
2
3
4
5
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen
     
</PreferenceScreen>

위와같은 코드가 작성되어있습니다.
이제 여기에 필요한 기능들을 추가로 작성합니다.

PreferenceScreen 태그 안에는,
복수의 PreferenceCategory가 들어갈 수 있습니다.
또 PreferenceCategory 안에는 복수의 Preference들이 들어갈 수 있습니다.




이 Preference의 태그의 속성들은 간단하게는, 
"키(KEY)"와 "타이틀(TITLE)" / "키(KEY)"와 "타이틀(TITLE)", 그리고 "값(VALUE)"로 이루어져있습니다.
다른 속성들이 많이 있지만, 이것들만 사용해도 어렵지 않게 설정의 기능들을 구현해 낼 수 있습니다.

저는 간단하게 텍스트로 이루어진 카테고리와, 체크박스로 이루어진 카테고리를 추가해 설정 화면을 만들어보려고 합니다.
결과적으로는 아래와 같은 화면을 만들 수 있습니다.

 
 

안드로이드에서 제공해주는 것만으로도 이렇게 그럴듯한 설정화면을 만들었습니다.

이 설정 화면의 XML 코드는 아래와 같습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen
    android:key="setting_activity_top_title"
    android:title="설정  - http://croute.me/340">
     
    <!-- 설정를 구성하는 Layout XML -->
    <!-- @author croute -->
    <!-- @since 2011.02.25 -->
     
    <PreferenceCategory
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:key="setting_activity_account"
        android:title="어플리케이션 정보">
        <Preference
            android:key="setting_activity_id"
            android:title="어플케이션 이름"
            android:selectable="true" />
        <Preference
            android:key="setting_activity_app_version"
            android:title="어플리케이션 버전"      
            android:selectable="true" />
    </PreferenceCategory>
     
    <PreferenceCategory
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:key="setting_activity_pushalarm"
        android:title="알림 설정">
        <CheckBoxPreference
            android:key="setting_activity_autoalarm"
            android:title="자동알림"
            android:summary="어플리케이션 알림이 종료된 경우 자동으로 재실행"
            android:defaultValue="true"/>
        <CheckBoxPreference
            android:key="setting_activity_alarm_reiceive"
            android:title="알림설정" android:defaultValue="true"/>
    </PreferenceCategory>
     
</PreferenceScreen>

눈여겨 볼점은 Preference를 두가지 사용했다는 것.
키와 타이틀을 지정했다는 것입니다.

여기서는 간단한 화면을 구성하기 위해 Preference와 CheckBoxPreference만을 사용했지만, 
(실제로는 EdittextPreference, ListPreference 등 많은 Preference가 있습니다.)





 2. 액티비티 작성

이제 액티비티로 가봅니다.

PreferenceActivity를 처음작성할 때 달라진 부분은 하나뿐입니다.

보통의 Activity에서 레이아웃을 설정할 때 setContentView(R.layout.파일이름)으로 설정한다면,
PreferenceActivity는 레이아웃을 설정할 때 addPreferencesFromResource(R.layout.파일이름)과 같이 합니다.

setContentView -> addPreferenceFromResource

이렇게 바뀐 것 빼고는 액티비티의 기본 설정에서 크게 달라진것은 없습니다.

화면만 보여주기 위해서 액티비티는 아래와 같이 작성하면 됩니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package me.croute.preferenceactivity;
 
import android.os.Bundle;
import android.preference.PreferenceActivity;
 
 
public class PreferenceActivityExample extends PreferenceActivity
{
    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        addPreferencesFromResource(R.layout.preference_activity_example);
    }
}

하지만 화면만 보여주는 어플리케이션은 없기때문에 이제부터 체크박스 체크/해제에 따른 이벤트 처리,
어플리케이션 이름등을 클릭했을때의 이벤트 처리에 대해서 알아보도록 하겠습니다.


일반적인 Activity라면 클릭이벤트를 OnClickListener로 받습니다.
하지만 PreferenceActivity는 클릭이벤트를 OnPreferenceClickListener로 받아야 합니다.
일반적인 클릭이 아닌, Preference에 대한 클릭이기 때문입니다.


클릭이벤트는 아래와 같이 구현할 수 있습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
package me.croute.preferenceactivity;
 
import android.os.Bundle;
import android.preference.CheckBoxPreference;
import android.preference.Preference;
import android.preference.Preference.OnPreferenceClickListener;
import android.preference.PreferenceActivity;
 
public class PreferenceActivityExample extends PreferenceActivity implements OnPreferenceClickListener
{
    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        addPreferencesFromResource(R.layout.preference_activity_example);
         
        Preference pAppName = (Preference)findPreference("setting_activity_id");
        Preference pAppVersion = (Preference)findPreference("setting_activity_app_version");
        CheckBoxPreference cbpAutoAlarm = (CheckBoxPreference)findPreference("setting_activity_autoalarm");
        CheckBoxPreference cbpAlarmReceive = (CheckBoxPreference)findPreference("setting_activity_alarm_reiceive");
         
        pAppName.setOnPreferenceClickListener(this);
        pAppVersion.setOnPreferenceClickListener(this);
        cbpAutoAlarm.setOnPreferenceClickListener(this);
        cbpAlarmReceive.setOnPreferenceClickListener(this);
    }
 
    @Override
    public boolean onPreferenceClick(Preference preference)
    {
        // 어플리케이션 이름
        if(preference.getKey().equals("setting_activity_id"))
        {
        }
        // 어플리케이션 버전
        else if(preference.getKey().equals("setting_activity_app_version"))
        {
        }
        // 자동알림
        else if(preference.getKey().equals("setting_activity_autoalarm"))
        {
        }
        // 알림 받기
        else if(preference.getKey().equals("setting_activity_alarm_reiceive"))
        {
        }
        return false;
    }
}

여기서 한가지 주의할점은 Preference의 CheckBoxPreference는 자동으로 체크됨을 잡아내서 자신의 상태를 변경합니다.
(기존의 Activity에서 체크박스를 사용하듯이 하면, 체크 되있던걸 돌리고, 체크 안되야 하는걸 체크하는 사태가 일어납니다.)

구현은 여기까지면 충분히 기본적인 설정의 기능들을 사용할 수 있습니다.

이제 클릭이벤트에 각자에 맞는 기능들을 추가해주면됩니다.



An Introduction to Python’s Flask Framework

\Rating:

An Introduction to Python’s Flask Framework

Tutorial Details
  • Difficulty: Beginner
  • Completion Time: 30 Minutes
  • Reference : Link


Flask is a small and powerful web framework for Python. It’s easy to learn and simple to use, enabling you to build your web app in a short amount of time.

In this article, I’ll show you how to build a simple website, containing two static pages with a small amount of dynamic content. While Flask can be used for building complex, database-driven websites, starting with mostly static pages will be useful to introduce a workflow, which we can then generalize to make more complex pages in the future. Upon completion, you’ll be able to use this sequence of steps to jumpstart your next Flask app.


Flask 설치

Before getting started, we need to install Flask. Because systems vary, things can sporadically go wrong during these steps. If they do, like we all do, just Google the error message or leave a comment describing the problem.

virtualenv 설치

Virtualenv is a useful tool that creates isolated Python development environments where you can do all your development work.

We’ll use virtualenv to install Flask. Virtualenv is a useful tool that creates isolated Python development environments where you can do all your development work. Suppose you come across a new Python library that you’d like to try. If you install it system-wide, there is the risk of messing up other libraries that you might have installed. Instead, use virtualenv to create a sandbox, where you can install and use the library without affecting the rest of your system. You can keep using this sandbox for ongoing development work, or you can simply delete it once you’ve finished using it. Either way, your system remains organized and clutter-free.

It’s possible that your system already has virtualenv. Refer to the command line, and try running:

1
$ virtualenv --version

If you see a version number, you’re good to go and you can skip to this “Install Flask” section. If the command was not found, use easy_install or pip to install virtualenv. If running Linux or Mac OS X, one of the following should work for you:

1
$ sudo easy_install virtualenv

or:

1
$ sudo pip install virtualenv

or:

1
$ sudo apt-get install python-virtualenv

If you don’t have either of these commands installed, there are several tutorials online, which will show you how to install it on your system. If you’re running Windows, follow the “Installation Instructions” on this pageto get easy_install up and running on your computer.

Install Flask

After installing virtualenv, you can create a new isolated development environment, like so:

1
$ virtualenv flaskapp

Here, virtualenv creates a folder, flaskapp/, and sets up a clean copy of Python inside for you to use. It also installs the handy package manager, pip.

Enter your newly created development environment and activate it so you can begin working within it.

1
2
$ cd flaskapp
$ . bin/activate

Now, you can safely install Flask:

1
$ pip install Flask

Setting up the Project Structure

Let’s create a couple of folders and files within flaskapp/ to keep our web app organized.

1
2
3
4
5
6
7
8
9
10
.
.
├── app
│   ├── static
│   │   ├── css
│   │   ├── img
│   │   └── js
│   ├── templates
│   ├── routes.py
│   └── README.md

Within flaskapp/, create a folder, app/, to contain all your files. Inside app/, create a folder static/; this is where we’ll put our web app’s images, CSS, and JavaScript files, so create folders for each of those, as demonstrated above. Additionally, create another folder, templates/, to store the app’s web templates. Create an empty Python file routes.py for the application logic, such as URL routing.

And no project is complete without a helpful description, so create a README.md file as well.

Now, we know where to put our project’s assets, but how does everything connect together? Let’s take a look at “Fig. 1″ below to see the big picture:

Fig. 1

  1. 사용자는 홈페이지에 접속하기 위해 도메인의 Root URL에 요청을 보낸다.
  2. routes.py 가 Python 함수에 URL을 요청한다.
  3. Python 함수가 '/templates' 폴더에서 Web Template을 찾는다.
  4. A web template은 '/static' 폴더에서 이미지, CSS, Javascript와 같은 HTML을 렌더링하는데 필요한 파일을 찾는다.
  5. 렌더링 된 HTML이 routes.py 로 전송된다.
  6. routes.py 가 HTML을 브라우저로 보낸다.

We start with a request issued from a web browser. A user types a URL into the address bar. The request hits routes.py, which has code that maps the URL to a function. The function finds a template in thetemplates/ folder, renders it to HTML, and sends it back to the browser. The function can optionally fetch records from a database and then pass that information on to a web template, but since we’re dealing with mostly static pages in this article, we’ll skip interacting with a database for now.

Now that we know our way around the project structure we set up, let’s get started with making a home page for our web app.


Creating a Home Page

When you write a web app with a couple of pages, it quickly becomes annoying to write the same HTML boilerplate over and over again for each page. Furthermore, what if you need to add a new element to your app, such as a new CSS file? You would have to go into every single page and add it in. This is time consuming and error prone. Wouldn’t it be nice if, instead of repeatedly writing the same HTML boilerplate, you could define your page layout just once, and then use that layout to make new pages with their own content? This is exactly what web templates do!

Web templates are simply text files that contain variables and control flow statements (if..elsefor, etc), and end with an.html or .xml extension.

The variables are replaced with your content, when the web template is evaluated. Web templates remove repetition, separate content from design, and make your application easier to maintain. In other, simpler words, web templates are awesome and you should use them! Flask uses the Jinja2 template engine; let’s see how to use it.

As a first step, we’ll define our page layout in a skeleton HTML document layout.html and put it inside thetemplates/ folder:

app/templates/layout.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<!DOCTYPE html>
<html>
  <head>
    <title>Flask App</title>   
  </head>
  <body>
   
    <header>
      <div class="container">
        <h1 class="logo">Flask App</h1>
      </div>
    </header>
     
    <div class="container">
      {% block content %}
      {% endblock %}
    </div>
     
  </body>
</html>

This is simply a regular HTML file…but what’s going on with the {% block content %}{% endblock %}part? To answer this, let’s create another file home.html:

app/templates/home.html

1
2
3
4
5
6
7
{% extends "layout.html" %}
{% block content %}
  <div class="jumbo">
    <h2>Welcome to the Flask app<h2>
    <h3>This is the home page for the Flask app<h3>
  </div>
{% endblock %}

The file layout.html defines an empty block, named content, that a child template can fill in. The filehome.html is a child template that inherits the markup from layout.html and fills in the “content” block with its own text. In other words, layout.html defines all of the common elements of your site, while each child template customizes it with its own content.

This all sounds cool, but how do we actually see this page? How can we type a URL in the browser and “visit” home.html? Let’s refer back to Fig. 1. We just created the template home.html and placed it in thetemplates/ folder. Now, we need to map a URL to it so we can view it in the browser. Let’s open uproutes.py and do this:

app/routes.py

1
2
3
4
5
6
7
8
9
10
from flask import Flask, render_template
 
app = Flask(__name__)     
 
@app.route('/')
def home():
  return render_template('home.html')
 
if __name__ == '__main__':
  app.run(host='0.0.0.0')

That’s it for routes.py. What did we do?

  1. First. we imported the Flask class and a function render_template.
  2. Next, we created a new instance of the Flask class.
  3. We then mapped the URL / to the function home(). Now, when someone visits this URL, the functionhome() will execute.
  4. The function home() uses the Flask function render_template() to render the home.htmltemplate we just created from the templates/ folder to the browser.
  5. Finally, we use run() to run our app on a local server. We’ll set the debug flag to true, so we can view any applicable error messages if something goes wrong, and so that the local server automatically reloads after we’ve made changes to the code.

We’re finally ready to see the fruits of our labor. Return to the command line, and type:

1
$ python routes.py

Visit http://localhost:5000/ in your favorite web browser.

When we visited http://localhost:5000/routes.py had code in it, which mapped the URL / to the Python function home()home() found the web template home.html in the templates/ folder, rendered it to HTML, and sent it back to the browser, giving us the screen above.

Pretty neat, but this home page is a bit boring, isn’t it? Let’s make it look better by adding some CSS. Create a file, main.css, within static/css/, and add these rules:

static/css/main.css

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
body {
  margin: 0;
  padding: 0;
  font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
  color: #444;
}
 
/*
 * Create dark grey header with a white logo
 */
  
header {
  background-color: #2B2B2B;
  height: 35px;
  width: 100%;
  opacity: .9;
  margin-bottom: 10px;
}
 
header h1.logo {
  margin: 0;
  font-size: 1.7em;
  color: #fff;
  text-transform: uppercase;
  float: left;
}
 
header h1.logo:hover {
  color: #fff;
  text-decoration: none;
}
 
/*
 * Center the body content
 */
  
.container {
  width: 940px;
  margin: 0 auto;
}
 
div.jumbo {
  padding: 10px 0 30px 0;
  background-color: #eeeeee;
  -webkit-border-radius: 6px;
     -moz-border-radius: 6px;
          border-radius: 6px;
}
 
h2 {
  font-size: 3em;
  margin-top: 40px;
  text-align: center;
  letter-spacing: -2px;
}
 
h3 {
  font-size: 1.7em;
  font-weight: 100;
  margin-top: 30px;
  text-align: center;
  letter-spacing: -1px;
  color: #999;
}

Add this stylesheet to the skeleton file layout.html so that the styling applies to all of its child templates by adding this line to its <head> element:

1
<link rel="stylesheet" href="{{ url_for('static', filename='css/main.css') }}">;

We’re using the Flask function, url_for, to generate a URL path for main.css from the static folder. After adding this line in, layout.html should now look like:

app/templates/layout.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<!DOCTYPE html>
<html>
  <head>
    <title>Flask</title>   
    <strong><link rel="stylesheet" href="{{ url_for('static', filename='css/main.css') }}"></strong>
  </head>
  <body>
    <header>
      <div class="container">
        <h1 class="logo">Flask App</h1>
      </div>
     </header>
    
    <div class="container">
      {% block content %}
      {% endblock %}
    </div>
  </body>
</html>

Let’s switch back to the browser and refresh the page to view the result of the CSS.

That’s more like it! Now, when we visit http://localhost:5000/routes.py still maps the URL / to the Python function home(), and home() still finds the web template home.html in the templates/ folder. But, since we added the CSS file main.css, the web template home.html looks in static/ to find this asset, before rendering to HTML and being sent back to the browser.

We’ve achieved a lot so far. We started with Fig. 1 by understanding how Flask works, and now we’ve seen how it all plays out, by creating a home page for our web app. Let’s move on and create an About page.


Creating an About Page

In the previous section, we created a web template home.html by extending the skeleton file layout.html. We then mapped the URL / to home.html in routes.py so we could visit it in the browser. We finished things up by adding some styling to make it look pretty. Let’s repeat that process again to create an about page for our web app.

We’ll begin by creating a web template, about.html, and putting it inside the templates/ folder.

app/templates/about.html

1
2
3
4
5
6
{% extends "layout.html" %}
  
{% block content %}
  <h2>About</h2>
  <p>This is an About page for the Intro to Flask article. Don't I look good? Oh stop, you're making me blush.</p>
{% endblock %}

Just like before with home.html, we extend from layout.html, and then fill the content block with our custom content.

In order to visit this page in the browser, we need to map a URL to it. Open up routes.py and add another mapping:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from flask import Flask, render_template
  
app = Flask(__name__)
  
@app.route('/')
def home():
  return render_template('home.html')
  
@app.route('/about')
def about():
  return render_template('about.html')
  
if __name__ == '__main__':
  app.run(host='0.0.0.0')

We mapped the URL /about to the function about(). Now we can open up the browser and go tohttp://localhost:5000/about and check out our newly created page.


Adding Navigation

Most websites have links to their main pages within the header or footer of the document. These links are usually visible across all pages of a website. Let’s open up the skeleton file, layout.html. and add these links so they show up in all of the child templates. Specifically, let’s add a <nav> element inside the <header> element:

app/templates/layout.html

1
2
3
4
5
6
7
8
9
10
11
12
13
...
<header>
  <div class="container">
    <h1 class="logo">Flask App</h1>
    <strong><nav>
      <ul class="menu">
        <li><a href="{{ url_for('home') }}">Home</a></li>
        <li><a href="{{ url_for('about') }}">About</a></li>
      </ul>
    </nav></strong>
  </div>
</header>
...

Once again, we use the Flask function url_for to generate URLs.

Next, add some more style rules to main.css to make these new navigation elements look good:

app/static/css/main.css

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
...
 
/*
 * Display navigation links inline
 */
 
.menu {
  float: right;
  margin-top: 8px;
}
 
.menu li {
  display: inline;
}
 
.menu li + li {
  margin-left: 35px;
}
 
.menu li a {
  color: #999;
  text-decoration: none;
}

Finally, open up the browser and refresh http://localhost:5000/ to see our newly added navigation links.


Conclusion

Over the course of this article, we built a simple web app with two, mostly static, pages. In doing so, we learned a workflow that can be used to create more complex websites with dynamic content. Flask is a simple, but powerful framework that enables you to efficiently build web apps. Go ahead – check it out!


Intro to Flask: Adding a Contact Page

\Rating:

Intro to Flask: Adding a Contact Page

Tutorial Details
  • Difficulty: Beginner
  • Completion Time: 1 Hour
  • Reference : Link

In the previous article in this mini-series, we leveraged Flask to build a simple website that contains “Home” and “About” pages using a generalized workflow that we can apply to other Flask-based web apps. In this lesson, I’ll demonstrate how to add a “Contact” page that allow users to send you messages.

The code used in this article can be found on GitHub. Captions, such as Checkpoint: 05_contact_form, mean that you can switch to the branch named “05_contact_form” and review the code at that point in the article.


Flask Extensions

You can find a full list of extensions in the Flask Extension Registry.

Flask doesn’t come with many features off the shelf, making it easy to pick up and learn. There is no object-relational mapper for database interaction or admin interfaces to add and update content. It only offers a small set of functions, two of which we’ve already used — url_for() andrender_template().

Instead of shipping with extra functionality, Flask’s extension model allows you to add functionality as needed. A Flask extension is a package that adds specific functionality to your app. For example, Flask-SQLAlchemy adds database support to your app, whereas Flask-Login adds login/logout support. You can find a full list of extensions in the Flask Extension Registry.

To create a Contact page, we’ll use Flask-WTF to handle and validate form data and Flask-Mail to email the form data to you.


Flask-WTF

Flask-WTF is an exension that handles and validates form data. What does that mean? Look at the following figure:

Fig. 1

  1. A user issues a GET request for a web page that contains a form.
  2. The user fills in the form.
  3. The user clicks the “Send” button, submitting it to the server via a POST request.
  4. The server validates the information.
  5. If one or more fields do not validate, the web page containing the form loads again with a helpful error message, prompting the user to try again.
  6. If all fields validate, the form information is used in the next step in the pipeline.

A contact page will have fields for the user’s name, email, subject, and message. In Flask, we’ll POST the form to a function inside routes.py. This function is called the form handler. We’ll run a few validation checks, and if any of the input does not pass muster, we’ll refresh the page to display a message that describes the error. Once all validation checks pass, we’ll use the form data for the next step: emailing the message to you, the website owner.

Flask extensions are simple, powerful tools that extend the functionality of your Flask-based app.

That’s how form handling and validation works. Now where do we actually define the form? We could write HTML using the<form> element and set its action attribute to a Python script. The Python script would mirror the form in order to capture each form field and validate the form field data. If we use this strategy, however, we’d essentially define the form twice — once for the front-end and once for the back-end.

It would be great to define the form only once: in the Python script. This is exactly what Flask-WTF allows us to do. We’ll define the form just once in a Python script, and then we’ll let Flask-WTF generate the form’s HTML for us. The point of all of this is to separate presentation from content.

Enough chatter. Let’s code.

Creating a Form

As a first step, let’s get back into the isolated development environment we created last time.

1
2
$ cd flaskapp
$ . bin/activate

Now that we’ve entered and activated our development environment, we can safely install Flask-WTF:

1
$ pip install flask-wtf

Let’s now define the form in a Python script. We already have routes.py, which maps URLs to functions. Let’s not clutter it with unrelated code. Instead, create a new file called forms.py, and place it inside theapp/ folder.

app/forms.py

1
2
3
4
5
6
7
8
from flask.ext.wtf import Form, TextField, TextAreaField, SubmitField
 
class ContactForm(Form):
  name = TextField("Name")
  email = TextField("Email")
  subject = TextField("Subject")
  message = TextAreaField("Message")
  submit = SubmitField("Send")

We just created a form. What did we do? First, we imported a few useful classes from Flask-WTF — the base Form class, a text field, a textarea field for multi-line text input, and a submit button. Next, we created a new class named ContactForm, inheriting from the base Form class. Then we created each field that we want to see in the contact form. Instead of writing <input type="text">Name</input> in an HTML file, you write name = TextField("Name").

Using the Form

Now let’s use our form. We want it to appear when a user visits the contact page. In Flask terms, we want the form to show up in a web template and map a URL to that web template so we can visit it in the browser. This means we need to create a new web template and a new URL mapping. Let’s start by creating a new URL mapping.

This is an action-packed section, and it may be a little confusing. But stick with me and we’ll get through it.

As a first step, open routes.py and import our newly created form by adding from forms import ContactForm at the beginning of the script.

app/routes.py

1
2
from flask import Flask, render_template
from forms import ContactForm

You can prevent a CSRF attack by making sure that the form submission originates from your web app.

Next, configure Flask-WTF to handle a security exploit known as cross-site request forgery (CSRF). In a perfect world, your server would only process forms that belong to your web app. In other words, your server would only handle and validate the forms that you created. However, it is possible for an attacker to create a form on his own website, fill it in with malicious information, and submit it to your server. If your server accepts this malicious information, all sorts of bad things can happen next.

You can prevent a CSRF attack by making sure that the form submission originates from your web app. One way to do this is to keep a unique token hidden inside your HTML <form>tag that cannot be guessed by attackers. When the form POSTs to your server, the token is checked first. If the token does not match, your server rejects the form submission and does not touch the form data. If the token matches, the server proceeds with form handling and validation.

Flask-WTF does all of this with an easy one-liner. Just configure Flask-WTF with a secret key, and Flask-WTF takes care of generating and managing unique tokens for your forms.

app/routes.py

1
2
3
4
5
6
from flask import Flask, render_template, request, flash
from forms import ContactForm
 
app = Flask(__name__)
 
app.secret_key = 'development key'

Here in line six, I set the secret key to ‘development key’. Feel free to make yours more complex, longer, and alphanumeric.

Now that we’ve imported and configured our contact form, we can use it in a URL mapping in routes.py. Let’s go ahead and create that URL mapping.

app/routes.py

1
2
3
4
@app.route('/contact')
def contact():
  form = ContactForm()
  return render_template('contact.html', form=form)

Now when someone visits the URL /contact, the function contact() will execute. Inside contact(), we first create a new instance of our contact form in line three and sent it to a web template namedcontact.html in line four. We will create this web template shortly.

We still have some work to do here though. Figure 1 showed that if a GET request is sent to the server, the web page containing the form should be retrieved and loaded in browser. If the server receives a POST request, a function should capture the form field data and check if it’s valid. In Python terms, this logic can be expressed in an if...else statement. There is a Flask class for distinguishing between GET and POST requests, so let’s start by importing that class at the beginning of routes.py and add the if...else logic to the contact() function.

app/routes.py

1
2
3
4
5
6
7
8
9
10
11
12
13
from flask import Flask, render_template, request
.
.
.
@app.route('/contact', methods=['GET', 'POST'])
def contact():
  form = ContactForm()
 
  if request.method == 'POST':
    return 'Form posted.'
 
  elif request.method == 'GET':
    return render_template('contact.html', form=form)

We already imported the Flask class and render_template() in the previous article, so here we import one more Flask class named requestrequest determines whether the current HTTP method is a GET or a POST. Next is the if...else logic to the contact() function (lines 9-13).

In the case of a POST request, a string indicating that the form has been posted will be returned.

This string is a temporary placeholder, and we’ll replace it with real code in the final step of this article. Otherwise, if the request uses GET, we return the web template contact.html that contains the form.

The next step is to create the web template contact.html and put it inside the templates/ folder.

app/templates/contact.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
{% extends "layout.html" %}
 
{% block content %}
  <h2>Contact</h2>
  <form action="{{ url_for('contact') }}" method=post>
    {{ form.hidden_tag() }}
 
    {{ form.name.label }}
    {{ form.name }}
 
    {{ form.email.label }}
    {{ form.email }}
 
    {{ form.subject.label }}
    {{ form.subject }}
 
    {{ form.message.label }}
    {{ form.message }}
 
    {{ form.submit }}
  </form>
{% endblock %}

As with home.html and about.html, the contact.html template extends layout.html and fills the ‘content’ block with its own text. We first specify where to send the form data on submission by setting the<form> element’s action attribute to the contact() function we created in routes.py (line five). Next, we let the Jinja2 template engine generate the bulk of the form for us (lines 6-20). We start by inserting a hidden tag in line six to protect against CSRF exploits. Lastly, we add each label and field of the form.

We are now ready to see the result of all our work. Just type the following:

1
$ python routes.py

Then go to http://localhost:5000/contact in your favorite web browser.

The contact page containing the form has loaded. Fill in the form fields and click the "Send" button. You’ll see a page that looks like this:

Awesome! Form submission is working.

Let’s quickly review everything we did in this section:

  • We type in the URL http://localhost:5000/contact into the browser’s address bar.
  • The GET request hits routes.py, where the URL /contact is mapped to the function contact().
  • The function contact() executes, where a variable named form containing a usable instance of theContactForm class is sent to the web template contact.html.
  • contact.html generates the contact form’s HTML.
  • Rendered HTML is sent back to routes.py.
  • routes.py sends the HTML back to the browser and we see the contact page containing the form.
  • We fill in the contact form and submit it by clicking the “Send” button.
  • The POST request hits routes.py, where the URL /contact is mapped to the function contact().
  • The function contact() executes once more, this time following the if...else control flow for the HTTP POST request.
  • The string 'Form posted.' is sent back to the browser, giving us the screen above.

— Checkpoint: 05_contact_form —

This is cool, but the contact form looks ugly. Let’s make it look better by adding some CSS. Open upmain.css and add these rules:

static/css/main.css

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
/* Contact form */
form label {
  font-size: 1.2em;
  font-weight: bold;
  display: block;
  padding: 10px 0;
}
 
form input#name,
form input#email,
form input#subject {
  width: 400px;
  background-color: #fafafa;
  -webkit-border-radius: 3px;
     -moz-border-radius: 3px;
          border-radius: 3px;
  border: 1px solid #cccccc;
  padding: 5px;
  font-size: 1.1em;
}
 
form textarea#message {
  width: 500px;
  height: 100px;
  background-color: #fafafa;
  -webkit-border-radius: 3px;
     -moz-border-radius: 3px;
          border-radius: 3px;
  border: 1px solid #cccccc;
  margin-bottom: 10px;
  padding: 5px;
  font-size: 1.1em;
}
 
form input#submit {
  display: block;
  -webkit-border-radius: 3px;
     -moz-border-radius: 3px;
          border-radius: 3px;
  border:1px solid #d8d8d8;
  padding: 10px;
  font-weight:bold;
  text-align: center;
  color: #000000;
  background-color: #f4f4f4;
  background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #f4f4f4), color-stop(100%, #e5e5e5));
  background-image: -webkit-linear-gradient(top, #f4f4f4, #e5e5e5);
  background-image: -moz-linear-gradient(top, #f4f4f4, #e5e5e5);
  background-image: -ms-linear-gradient(top, #f4f4f4, #e5e5e5);
  background-image: -o-linear-gradient(top, #f4f4f4, #e5e5e5);
  background-image: linear-gradient(top, #f4f4f4, #e5e5e5);filter:progid:DXImageTransform.Microsoft.gradient(GradientType=0,startColorstr=#f4f4f4, endColorstr=#e5e5e5);
}
 
form input#submit:hover{
  cursor: pointer;
  border:1px solid #c1c1c1;
  background-color: #dbdbdb;
  background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#dbdbdb), color-stop(100%, #cccccc));
  background-image: -webkit-linear-gradient(top, #dbdbdb, #cccccc);
  background-image: -moz-linear-gradient(top, #dbdbdb, #cccccc);
  background-image: -ms-linear-gradient(top, #dbdbdb, #cccccc);
  background-image: -o-linear-gradient(top, #dbdbdb, #cccccc);
  background-image: linear-gradient(top, #dbdbdb, #cccccc);filter:progid:DXImageTransform.Microsoft.gradient(GradientType=0,startColorstr=#dbdbdb, endColorstr=#cccccc);
}

Switch back to the browser and refresh http://localhost:5000/contact to see the result of the CSS.

This looks much better. Let’s move on to form validation.

— Checkpoint: 06_contact_styling —

Validating Form Data

A user can now visit the URL /contact and fill in the form. But what happens if the user does not properly fill out the form? We need to validate the user input so that it won’t cause problems in later steps.

Form validation is performed by using form validators. Fortunately, Flask-WTF comes with many useful, built-in validators that we can use right away. We’ll put these validators in the ContactForm class definition in forms.py.

The most basic validator is presence, which simply ensures that all form fields are filled in, so let’s start here.

app/forms.py

1
2
3
4
5
6
7
8
from flask.ext.wtf import Form, TextField, TextAreaField, SubmitField, validators, ValidationError
 
class ContactForm(Form):
  name = TextField("Name",  [validators.Required()])
  email = TextField("Email",  [validators.Required()])
  subject = TextField("Subject",  [validators.Required()])
  message = TextAreaField("Message",  [validators.Required()])
  submit = SubmitField("Send")

We start by importing validators and ValidationError from Flask-WTF. This gives us access to Flask-WTF’s built-in validators. Next we add [validators.Required()] to each form field in order to validate its presence. Notice that this validator is inside a Python list, meaning that we can easily add more validators to this list.

Next, let’s require email addresses to match the pattern user@example.com by adding the Email validator to the email field.

app/forms.py

1
2
3
4
5
6
7
8
from flask.ext.wtf import Form, TextField, TextAreaField, SubmitField, validators, ValidationError
 
class ContactForm(Form):
  name = TextField("Name",  [validators.Required()])
  email = TextField("Email",  [validators.Required(), validators.Email()])
  subject = TextField("Subject",  [validators.Required()])
  message = TextAreaField("Message",  [validators.Required()])
  submit = SubmitField("Send")

That does it for our form validations.

— Checkpoint: 07_form_validations —

Looking back at Figure 1, if any validation check fails, the contact page should reload with an error message so that the user can fix the mistake and try again. This error message must only appear when validation fails and disappear when the mistake has been fixed.

Our next step is to send this sort of temporary error message to the user when validation fails. Flask makes this really easy by using its flash() function. Let’s start by opening routes.py and importing Flask’sflash() function at the beginning of the script.

app/routes.py

1
from flask import Flask, render_template, request, flash

After the contact form POSTs to the server, any validation failure should reload the form with a helpful error message. Otherwise, the input data can be used for future processing. Once again, this logic can be expressed in an if...else statement. Let’s add this if...else logic to the contact() function inside theif request.method == 'POST': block.

app/routes.py

1
2
3
4
5
6
7
8
9
10
11
12
13
@app.route('/contact', methods=['GET', 'POST'])
def contact():
  form = ContactForm()
 
  if request.method == 'POST':
    if form.validate() == False:
      flash('All fields are required.')
      return render_template('contact.html', form=form)
    else:
      return 'Form posted.'
 
  elif request.method == 'GET':
    return render_template('contact.html', form=form)

If any validation check fails, form.validate() will be False. The error message All fields are required will be sent to contact.html. Otherwise, we’ll see the temporary placeholder string Form posted, indicating the form has been successfully submitted.

Next, let’s modify contact.html so that it can receive and display these temporary error messages. See the following block:

1
2
3
{% for message in get_flashed_messages() %}
  <div class="flash">{{ message }}</div>
{% endfor %}

The function get_flashed_messages() pulls all flashed messages and returns them. We then simply display each flashed message by using a Jinja2 for loop. Add this code block to contact.html after<h2>Contact</h2> and before the <form> tag.

app/templates/contact.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
{% extends "layout.html" %}
 
{% block content %}
  <h2>Contact</h2>
 
  {% for message in get_flashed_messages() %}
    <div class="flash">{{ message }}</div>
  {% endfor %}
   
  <form action="{{ url_for('contact') }}" method=post>
    {{ form.hidden_tag() }}
 
    {{ form.name.label }}
    {{ form.name }}
 
    {{ form.email.label }}
    {{ form.email }}
 
    {{ form.subject.label }}
    {{ form.subject }}
 
    {{ form.message.label }}
    {{ form.message }}
 
    {{ form.submit }}
  </form>
{% endblock %}

Lastly, let’s add a CSS rule in main.css so that flashed error messages look pretty.

main.css

1
2
3
4
5
6
/* Message flashing */
.flash {
  background-color: #FBB0B0;
  padding: 10px;
  width: 400px;
}

Open your browser and visit http://localhost:5000/contact. Leave all the fields blank and click “Send” to test whether form validation and error message flashing work.

This is sweet! We have successfully sent an error message to our contact form if a validation check fails.

— Checkpoint: 08_error_message_flashing —

But we’re not done; we can actually do a little better. Instead of having one generic error message for all failed validation checks, it would be better to have a specific error message for each failed validation check. For example, if the user forgets to fill in the subject field, a specific error message that says Please enter a subject would be flashed. Likewise, if the user forgets to fill in their name, we’d flash a specific error message that says Please enter your name. We can accomplish this pretty easily, so let’s start by writing our specific error messages inside each validator in forms.py.

app/forms.py

1
2
3
4
5
6
7
8
from flask.ext.wtf import Form, TextField, TextAreaField, SubmitField, validators, ValidationError
 
class ContactForm(Form):
  name = TextField("Name",  [validators.Required("Please enter your name.")])
  email = TextField("Email",  [validators.Required("Please enter your email address."), validators.Email("Please enter your email address.")])
  subject = TextField("Subject",  [validators.Required("Please enter a subject.")])
  message = TextAreaField("Message",  [validators.Required("Please enter a message.")])
  submit = SubmitField("Send")

We simply write specific error messages inside each validator. Next, let’s modify contact.html to receive and display these specific error messages. Earlier, we relied on the function get_flashed_messages() to pull flashed error messages, and looped over them to display them. Let’s replace that block with this one:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
{% for message in form.name.errors %}
  <div class="flash">{{ message }}</div>
{% endfor %}
 
{% for message in form.email.errors %}
  <div class="flash">{{ message }}</div>
{% endfor %}
 
{% for message in form.subject.errors %}
  <div class="flash">{{ message }}</div>
{% endfor %}
 
{% for message in form.message.errors %}
  <div class="flash">{{ message }}</div>
{% endfor %}

Here we use the errors attribute for each form field to pull the specific error messages and loop over them using the Jinja2 for loop to display them.

Putting it all together, contact.html now look like this:

app/templates/contact.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
{% extends "layout.html" %}
 
{% block content %}
  <h2>Contact</h2>
 
  {% for message in form.name.errors %}
    <div class="flash">{{ message }}</div>
  {% endfor %}
 
  {% for message in form.email.errors %}
    <div class="flash">{{ message }}</div>
  {% endfor %}
 
  {% for message in form.subject.errors %}
    <div class="flash">{{ message }}</div>
  {% endfor %}
 
  {% for message in form.message.errors %}
    <div class="flash">{{ message }}</div>
  {% endfor %}
   
  <form action="{{ url_for('contact') }}" method=post>
    {{ form.hidden_tag() }}
 
    {{ form.name.label }}
    {{ form.name }}
 
    {{ form.email.label }}
    {{ form.email }}
 
    {{ form.subject.label }}
    {{ form.subject }}
 
    {{ form.message.label }}
    {{ form.message }}
 
    {{ form.submit }}
  </form>
{% endblock %}

Switch back to the browser, go to http://localhost:5000/contact, and click “Send”. Be sure to leave all form fields blank.

Perfect! The user now has helpful error messages if he makes a mistake.

— Checkpoint: 09_specific_message_flashing —

We accomplished a lot in this section. We created a contact form from scratch, learned how to protect against CSRF attacks, distinguished between GET and POST requests, enforced form validations, and flashed specific error messages if necessary. We now need to email the message.


Flask-Mail

Flask-Mail is a Flask exension that enables you to send emails from your Flask app. The steps below are similar to those we took to use Flask-WTF.

Let’s start by installing Flask-Mail.

1
$ pip install flask-mail

Configuring Flask-Mail

Next, lets import Flask-Mail into routes.py and configure it so that we can start using it.

app/routes.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from flask import Flask, render_template, request, flash
from forms import ContactForm
from flask.ext.mail import Message, Mail
 
mail = Mail()
 
app = Flask(__name__)
 
app.secret_key = 'development key'
 
app.config["MAIL_SERVER"] = "smtp.gmail.com"
app.config["MAIL_PORT"] = 465
app.config["MAIL_USE_SSL"] = True
app.config["MAIL_USERNAME"] = 'contact@example.com'
app.config["MAIL_PASSWORD"] = 'your-password'
 
mail.init_app(app)

First, we import the Message and Mail classes from Flask-Mail (line three). We’ll use the Message class to compose a new email and the Mail class to send the email. Next, we create the mail variable that contain a usable instance of the Mail class (line five).

We then configure Flask-Mail with few SMTP server settings (lines 11-15). I used Gmail’s SMTP server settings here, but you can easily use your favorite email provider. Just search for its SMTP settings and you’ll be set.

For example, if you want to use Yahoo! Mail, just search for “yahoo mail smtp server settings” and update the configuration.

Make sure to enter a real email and password in app.config["MAIL_USERNAME"] andapp.config["MAIL_PASSWORD"], respectively. This will be the account from which you’ll send email.

Finally, we attach mail to our Flask app so that we can start using it (line 17).

You’ve probably seen groups use contact email addresses like contact@example.com orsupport@example.com. If you own your own domain and can create a new contact email address, go ahead and put that email address in app.config["MAIL_USERNAME"]. Otherwise, you can use your personal email address just to see how this works.

Sending an Email

Now that the configuration is complete, let’s compose a new email containing the contact form data and send it. We should only send an email if the form has been submitted and all validation checks pass. This means we need to work inside the if request.method == 'POST': block again. We’ve already added logic inside the if form.validate() == False: block to handle validation failures. If all validation checks pass, form.validate() will be True and the program will enter the else block. Therefore, let’s go ahead and add logic inside the else: block.


app/routes.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@app.route('/contact', methods=['GET', 'POST'])
def contact():
  form = ContactForm()
 
  if request.method == 'POST':
    if form.validate() == False:
      flash('All fields are required.')
      return render_template('contact.html', form=form)
    else:
      msg = Message(form.subject.data, sender='contact@example.com', recipients=['your_email@example.com'])
      msg.body = """
      From: %s <%s>
      %s
      """ % (form.name.data, form.email.data, form.message.data)
      mail.send(msg)
 
      return 'Form posted.'
 
  elif request.method == 'GET':
    return render_template('contact.html', form=form)

We start by composing a new message (line 10). The Message class takes a subject line, a “from” address, and a “to” address. We then collect the contact form’s subject field data with form.subject.data and set it as the new message’s subject line. The email will be sent from the account you configured inapp.config["MAIL_USERNAME"], so that’s what we used here for the from address. The email will be sent to your personal email address so that you can receive and respond to new messages.

Next, we write the email itself (lines 11-14). We include the user’s name, email and message. I use Python’s string formatting operator % to format the email. And finally, we use mail.send(msg) to send the email (line 15).

Let’s see if everything works. Visit http://localhost:5000/contact, fill out each field, and click “Send.” If all goes well, you’ll receive a new email from your Flask app.

— Checkpoint: 10_send_email —

Tidying Up

Our penultimate step is to remove the temporary placeholder string 'Form posted.' with a message thanking the user for his feedback. This message should only appear if our application sends the email. Once again, this logic can be expressed in an if...else statement.

When the contact form has been successfully submitted, we’ll send a success flag from routes.py to contact.html.

We’ll place the if...else logic inside contact.html. If the success flag is set to True, we’ll display the thank you message. Otherwise, we’ll display the contact form.

Let’s start in routes.py inside the contact() function. Replace the temporary placeholder line return 'Form posted.' with return render_template('contact.html', success=True) in order to send a success flag to contact.html. The contact() function now looks like this:

app/routes.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@app.route('/contact', methods=['GET', 'POST'])
def contact():
  form = ContactForm()
 
  if request.method == 'POST':
    if form.validate() == False:
      flash('All fields are required.')
      return render_template('contact.html', form=form)
    else:
      msg = Message(form.subject.data, sender='contact@example.com', recipients=['your_email@example.com'])
      msg.body = """
      From: %s &lt;%s&gt;
      %s
      """ % (form.name.data, form.email.data, form.message.data)
      mail.send(msg)
 
      return render_template('contact.html', success=True)
 
  elif request.method == 'GET':
    return render_template('contact.html', form=form)

Next open contact.html and add the if...else logic. We’ll use Jinja2′s if...else syntax to make this happen.

app/templates/contact.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
{% extends "layout.html" %}
 
{% block content %}
  <h2>Contact</h2>
 
  {% if success %}
    <p>Thank you for your message. We'll get back to you shortly.</p>
 
  {% else %}
 
    {% for message in form.name.errors %}
      <div class="flash">{{ message }}</div>
    {% endfor %}
 
    {% for message in form.email.errors %}
      <div class="flash">{{ message }}</div>
    {% endfor %}
 
    {% for message in form.subject.errors %}
      <div class="flash">{{ message }}</div>
    {% endfor %}
 
    {% for message in form.message.errors %}
      <div class="flash">{{ message }}</div>
    {% endfor %}
 
    <form action="{{ url_for('contact') }}" method=post>
      {{ form.hidden_tag() }}
 
      {{ form.name.label }}
      {{ form.name }}
 
      {{ form.email.label }}
      {{ form.email }}
 
      {{ form.subject.label }}
      {{ form.subject }}
 
      {{ form.message.label }}
      {{ form.message }}
 
      {{ form.submit }}
    </form>
 
  {% endif %}
{% endblock %}

Starting in line six, {% if success %} means that if the success flag we sent from routes.py is set toTrue, then display <p>Thank you for your message. We'll get back to you shortly.</p>. Otherwise, follow the {% else %} branch and display the contact form. Jinja2 syntax asks that we close theif...else statement with {% endif %}, so we include that at the end (line 45).

— Checkpoint: 11_success_message —

Finally, let’s visit http://localhost:5000/contact one more time. Fill in each field and click “Send”.

Our last step is to add a navigation link to the contact page. In the previous article, we added these links tolayout.html inside the <header> element. Let’s also do that for the contact page (line eight).

app/templates/layout.html

1
2
3
4
5
6
7
8
9
10
11
12
<header>
  <div class="container">
    <h1 class="logo">Flask App</h1>
    <nav>
      <ul class="menu">
        <li><a href="{{ url_for('home') }}">Home</a></li>
        <li><a href="{{ url_for('about') }}">About</a></li>
        <li><a href="{{ url_for('contact') }}">Contact</a></li>
      </ul>
    </nav>
  </div>
</header>

— Checkpoint: 12_contact_nav_link —

Open up the browser and refresh http://localhost:5000/ to see the newly added navigation link.


Conclusion

In article, we added a contact page that contains a form to our Flask app. Forms appear in several places in web applications, most notably during sign up and login. This workflow can be adapted to meet those needs. In creating a contact page, we learned how to use Flask extensions.

Flask extensions are simple, powerful tools that extend the functionality of your Flask-based app.

Check out the Flask Extension Registry to explore many more extensions that you can integrate into your app.


$ python hello.py 


에서 이미 사용중이라고 에러가 나오면


$ who

$ skill -kill pts/0 




리눅스에 useradd 로 password와 계정들을만든후
vi /etc/passwd 들어가보시면 제일 밑에
님이 만드신 아이디가 잇으면 보일거에요
그중에 root 가 있어요
없을 수도 있지만 상관 없어여
만약에 user 라고 계정을 만들었으면
vi /etc/passwd   에   제 일 밑에  :9999 눌러서 가 보면
user :X:500:500::/home/user:/bin/bash
이런식으로 되있을거에요
다른 건 상관 없구
X : 500:500
에서  앞에 500 이라고 되어있는걸
0 으로 바꿔주시면  root 권한이 주어집니다
user :X:0:500::/home/user:/bin/bash
이렇게 바꿔주시면됩니다  채택 해주세요
이런식으로 하게되면!! root권한을 줄수이싿..

사용자 생성  계정 관리

 

리눅스 내부 명령어 : 셀에 내장되어 있는 명령어로 셀이 명령어를 이해한다.

리눅스 외부 명령어 : /bin 안에 파일의 형태로 존재

차이점 : 내부 명령어의 실행 시 별도의 프로세서를 시작시키지 않지만, 외부 명령어의 실행 시 새로운 서브 프로세서를 fork하고 실행

 

 

검색 경로 지정

리눅스에서 명령어는 셀이 특정 검색 경로를 찾아 실행하게 되어 있다.

검색경로가 지정되어 있는 파일 : .bash_profile

PATH = _______: _______:

path에 지정된 경로에 자주 사용하는 명령어의 결로를 앞쪽에 두는 것이 좋다. path에 설정된 경로를 찾을 때까지 앞에서 뒤쪽으로 차례대로 검색

 

/etc/default/useradd : 사용자의 기본적인 셀 환경을 설정할 수 있음.

 

 

계정 조회

users

현재 시스템에 로그인된 사용자 계정을 조회 / 사용자 계정에 대한 정보를 확인

          cat n /etc/passwd

                       root  :  x  :  o  :  o  :  root  :  /root  :  /bin/bash

                         1     2     3     4      5        6          7

                                     1 : 사용자명

                                     2 : 패스워드 (/etc/shadow 파일에 암호화되어 있음)

                                     3 : 사용자 계정 uid

                                     4 : 사용자 계정 gid

                                     5 : 사용자 계정 이름 정보

                                     6 : 사용자 계정 홈 디렉토리

                                     7 : 사용자 계정 로그인 셀

 

             cat n /etc/shadow

                           root  :  #$%!234^x13  :  11535  :  o  :  99999  :  7  :  :  :  :

                             1            2             3       4       5       6   7  8  9

                                        1 : 사용자명

                                        2 : 패스워드

                                        3 : 패스워드 파일 최종 수정일

                                        4 : 패스워드 변경 최소일

                                        5 : 패스워드 변경 최대일

                                        6 : 패스워드 만료 경고 기간

                                        7 : 패스워드 파기 기간 (패스워드 파기 후 계정 비활성 기간)

                                        8 : 계정 만료 기간

                                        9 : 예약 필드

 

 

계정 생성 기본 설정 파일

/etc/default/useradd : 사용자의 기본적인 그룹, 홈디렉토리, 만기일, 셀 지정

/etc/login.defs (쉐도우 패스워드 사용 시) : 사용자의 각종 환경변수 지정

user D : 기본 설정 사항 확인

 

 

계정 생성  암호 설정

useradd  생성할 계정명

passwd   생성한 계정명

useradd [옵션] 로그인 계정

             -c comment : 사용자 이름 또는 정보

             -d home_directory : 사용자 계정 홈 디렉토리

             -e expire_date : 사용자 계정 유효 기간

             -f inactive_time : 비활성 기간

             -g initial_group : 기본 그룹

             -G grout : 다음 그룹

             -s shell : 기본 로그인 셀

             -u uid : 사용자 계정 uid

 

 

계정 변경

usermod [옵션] 로그인 계정

-c comment : 사용자 이름 또는 정보

-d home_directory : 사용자 계정 홈 디렉토리

-e expire_date : 사용자 계정 유효 기간

-f inactive_time : 비활성 기간

-g initial_group : 기본 그룹

-G grout : 다음 그룹

-s shell : 기본 로그인 셀

-u uid : 사용자 계정 uid

                       usermod d /home/user m user

                       usermod e 2003-04-05 user

                       usermod f 3 user

                       usermod g users user

 

 

계정 삭제

userdel r 계정 (-r : 해당 계정자의 홈디렉토리까지 한 번에 삭제)

 

 

그룹조회

cat n /etc/group

 

 

그룹생성

groupadd [-g GID [-o]]            그룹 id (-o : GID 499이하 값으로 지정)

                       [-r]                    그룹 id 499이하 값으로 자동 지정

                       [-f]                    강제로 생성

                           groupadd g 900 toheart (900  groupid / toheart  그룹명)

 

 

그룹변경

groupmod [-g gid [-o]]            gid변경

                        [-n]                    새로운 그룹명으로 변경

                                     groupmod g 700 toheart

                                     groupmod n kkum toheart

 

 

그룹삭제

groupdel group             group 제거

CSS 선택자(Selector)의 종류 및 간단한 효과주기

이 강좌는 월간 w.e.b. HOW TO 2011년 02호에 게재되어 있습니다.

언제부터인가 우리의 웹은 이미지에 의존하는 웹페이지, 또한 당연하다는 듯이 각종 오브젝트 타입의 컨텐츠를 무분별하게 사용하여 무거운 웹사이트를 구축했었고 또, 대부분의 웹사이트들은 웹표준이나 웹접근성 보다는 화려한 그래픽으로 특정브라우저에 맞춰진 Content Design으로 처리해 왔다.

그러나 기존 포토샵 같은 이미지편집 Tool이나 플래시 같은 모션그래픽 툴로 밖에 할 수 없었던 효과들이 CSS3를 사용함으로써 많은 부분 코드 몇 줄 만으로 가능하게 되었다.

같은 조건의 서비스라면 더 가벼워지고 빠른 로딩속도는 덤으로 가질 수 있게 된 건 물론이다.

이번 연재에서는 기본적이지만 매우 중요한 선택자의 종류와 몇 가지 CSS3 주요속성을 가지고 여러 효과를 주는 방법에 대해 살펴보자.

연재순서:

CSS3 배경 및 변화 된 점

시간이 갈수록 시맨틱한 웹, 웹표준과 웹접근성이 점점 강조되는 때인 지금 모든 웹사이트가HTML(구조)+CSS(표현)+JS(동작) 세가지 계층을 분리하여 웹을 제대로 만드는 것이 중요한 이슈가 되면서 점점 세가지를 분리하고 디자인 컨텐츠를 좀 더 다양하고 모듈화 된 형태로 개발할 필요성을 느꼈기 때문에 CSS3가 그에 발맞추어 개발 진행 중이다.

아직 완전한 형태의 표준은 아니나 CSS3의 대부분의 기능을 지원하는 브라우저가 이미 나온 상태이며 최신 버전의 브라우저 들이 나올 때마다 더 많은 부분을 수용하고 포함할 것이다.

CSS3를 이용해 모바일용 웹 애플리케이션은 만들어지고 있지만, 일반 데스크톱 웹 환경에서는 아직 모바일에 비해 덜 진행된 상태이다. 우리나라의 현재 브라우저 점유율에서 가장 큰 위치를 차지하고 있는 IE계열(6~8)이 CSS3를 거의 지원하지 않는다.

하지만 그렇다고 실망하지는 말자! IE(8이하)도 완전히 사용하지 못하는 것은 아니다. Javascript를 이용한 Filter를 이용하여 여러 속성들의 표현을 흉내 낼 수 있다. 그리고 IE9 Beta 버전에서는 CSS3의 대부분의 속성들을 지원하려고 개발진행 중에 있다.

선택자(Selector)

선택자는 Type(element),Universal(*),class/id,attribute(속성),가상요소/가상클래스,선택자결합(하위,자식,인접) 등으로 구성된다.

PatternMeaningS5C8F3.6O11I9bI8I7I6
#idid로 지정된 요소 선택OOOOOOOO
.classclass로 지정된 요소 선택OOOOOOOO
EE 요소를 선택OOOOOOOO
E:link방문하지 않은 E를 선택OOOOOOOO
E:visited방문한 E를 선택OOOOOOOO
E:hover마우스가 올라가 있는 동안 E를 선택OOOOOOOO
E:active마우스 클릭 또는 키보드(enter)가 눌린 동안 E를 선택OOOOOOOX
E:focusfocus가 머물러 있는 동안 E를 선택OOOOOOXX
E:first-lineE 요소의 첫 번째 라인 선택OOOOOOOX
E:first-letterE 요소의 첫 번째 문자 선택OOOOOOOX
*모든 요소 선택OOOOOOOO
E[foo]‘foo’ 속성이 포함된 E를 선택OOOOOOOX
E[foo="bar"]‘foo’ 속성의 값이 ’bar’와 일치하는 E를 선택OOOOOOOX
E[foo~="bar"]‘foo’ 속성의 값에 ’bar’가 포함되는 E를 선택OOOOOOOX
E[foo|="en"]‘foo’ 속성의 값이 ’en’ 또는 ’en-’ 으로 시작되는  E를 선택OOOOOOOX
E:first-child첫 번째 자식 요소가 E라면 선택OOOOOOOX
E:lang(fr)HTML lang 속성의 값이 ’fr’로 지정된 E를 선택OOOOOOXX
E::beforeE 요소 전에 생성된 요소 선택OOOOOOXX
E::afterE 요소 후에 생성된 요소 선택OOOOOOXX
E>FE 요소의 자식인 F 요소 선택OOOOOOOX
E+FE 요소를 뒤의 F 요소 선택OOOOOOOX
E[foo^="bar"]‘foo’ 속성의 값이 ’bar’로 정확하게 시작하는 요소 선택OOOOOOOX
E[foo$="bar"]‘foo’ 속성의 값이 ’bar’로 정확하게 끝나는 요소 선택OOOOOOOX
E[foo*="bar"]‘foo’ 속성의 값에 ’bar’를 포함하는 요소 선택OOOOOOOX
E:root문서의 최상위 루트 요소 선택OOOOOXXX
E:nth-child(n)그 부모의 n번째 자식이 앞으로부터 지정된 순서와 일치하는 E 라면 선택OOOOOXXX
E:nth-last-child(n)n번째 자식이 뒤로부터 지정된 순서와 일치하는 요소가 E 라면 선택OOOOOXXX
E:nth-of-type(n)E 요소 중 앞으로부터 순서가 일치하는 n번째 E 요소 선택OOOOOXXX
E:nth-last-of-type(n)E 요소 중 끝으로부터 순서가 일치하는 n번째 E 요소 선택OOOOOXXX
E:last-childE 요소 중 마지막 자식이라면 E 선택OOOOOXXX
E:first-of-typeE 요소 중 첫번째 E 선택OOOOOXXX
E:last-of-typeE 요소 중 마지막 E 선택OOOOOXXX
E:only-childE 요소가 유일한 자식이면 선택OOOOOXXX
E:only-of-typeE 요소가 같은 타입이면 선택OOOOOXXX
E:empty텍스트 및 공백을 포함하여 빈 자식을 가진 E를 선택OOOOOXXX
E:targetE의 URI의 대상이 되면 선택OOOOOXXX
E:enabled활성화된 폼 컨트롤 E요소 선택OOOOOXXX
E:disabled비활성화된 폼 컨트롤 E요소 선택OOOOOXXX
E:checked선택된 폼 컨트롤(라디오버튼,체크박스)을 선택OOOOOXXX
E:not(s)s가 아닌 E 요소 선택OOOOOXXX
E~FE 요소가 앞에 존재하면 F를 선택OOOOOOOX

<표 1> CSS 버전에 따른 선택자의 종류와 브라우저별 지원여부 – CSS1 – CSS2 – CSS3

참조:

벤더 별 확장 속성 및 브라우저 엔진

대부분의 브라우저 벤더들은 자신들의 브라우저가 현재 지원하는 속성이 표준과 상이하거나 변경될 수 있다라고 생각하고 벤더확장 속성을 만들었다.
실제로 속성을 지원하지만 100% 표준스펙이 나온 상태가 아니기 때문에 개선점이나 버그발생시 피드백을 쉽게 하기 위해 만든 것으로 보인다.

브라우저 밴더S5C8F3.6O11I9I8I7I6
벤더확장 속성-webkit--webkit--moz--o--ms-/filter-ms-/filter-ms-/filter-ms-/filter
브라우저 엔진WebkitWebkitGeckoPrestoTasman

<표 2> 브라우저 벤더 별 확장 속성

이번 연재에서 다룰 CSS3주요속성

CSS3에는 정말 다양한 속성들이 공개 되었는데 CSS의 기존버전과는 다르게 모듈형태로 개발되고 있다.
이것은 각종 브라우저나 다양한 디바이스가 필요에 따라 원하는 CSS 모듈만을 탑재하거나 특정 모듈만을 빠르게 업데이트 할 수 있는 장점이 있다.
CSS3는 현재 Text, Fonts, Color, Backgrounds&Borders, Transforms, Transitions, Animations과 같은 종류의 모듈들을 개발하고 있다.
그 모듈형태의 CSS3 여러 속성 중에서 이번 연재에서 배워볼 CSS3 주요속성은 아래와 같다.

ModuleAttributeEffectS5C8F3.6O11I9I8I7I6
Background &borderborder-image테두리 이미지효과OOOOXXXX
border-radius테두리 라운드효과OOOOOXXX
box-shadow박스 그림자OOOOOXXX
multiple backgrounds배경 여러개 넣기OOOOOXXX
radient그라디언트 효과OOOXXXXX
Colorrgba(R,G,B,A)/HSLA칼라와 투명도효과OOOOOXXX
opacity이미지 투명도효과OOOOXXXX
Texttext-shadow글자 그림자효과OOOOXXXX
text-overflow글자 넘칠 때 자동 개행OOXOOOOO
User-Interfaceresize박스 사이즈 조절효과OOOOXXXX
Other modulesmulti-column layout문단 다중 칼럼효과OOOOXXXX

<표 3> 본 연재에서 배울 CSS3 속성

IE(6~8)에서는 공식적으로 지원하지 않는 부분은 X 표시가 되어 있지만 대부분 필터를 이용해 효과를 낼 수 있으며 다른 브라우저들은 벤더확장 속성을 이용하여 적용 가능하다.

CSS3의 새로운 주요속성을 이용하여 간단한 효과주기

IE(6~8)을 제외한 모든 브라우저가 이미 대부분의 CSS3 속성들을 지원하고 있다. 하지만 아직 표준이 완벽하게 정해진 것도 아니고 각 벤더들 마다 속성들을 표시하는 방법이나 랜더링하는 차이가 있다.

본 장에서는 CSS3의 새로운 주요 속성을 이용하여 간단한 예제와 함께 속성들을 익히는 과정이다.

테스트 환경으로서는 현재까지 웹킷(webkit)계열 브라우저가 CSS3를 가장 잘 지원하기 때문에 필자는 모든 샘플코드 및 예제를 크롬기준으로 작성하고 설명할 것이다. 독자들은 속성들의 기능들을 확인하고 대표되는 여러 브라우저에서 다양하게 테스트 하여 각각의 차이점도 익혀보면 좋을 것이다.

Common Source
  1. <!DOCTYPE html>
  2. <html lang="ko">
  3. <head>
  4. <title> 예제 </title>
  5. <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  6. <style type="text/css">
  7. body{
  8. font-size:12px;
  9. line-height:1.5em;
  10. }
  11. 적용할 DIV CSS 들어가는 곳
  12. </style>
  13. </head>
  14. <body>
  15. <div>CSS작업그룹은 CSS 규격을 모듈화하기로 결정했습니다.<br>
  16. 이런 모듈화는 다른 부분과 사이의 관계를 명확히 하는데 도움이되며<br>
  17. 완벽하게 문서의 크기를 줄일 수 있습니다.<br>
  18. 그것은 또한 우리가 모듈 단위로 특정 테스트 코드를 만들 수 있고 CSS의 구현에 도움이 …
  19. </div>
  20. </body>
  21. </html>

위와 같이 공통된 html 마크업을 가지고 css부분만 수정하거나 최소한의 마크업만 변경하여 모든 테스트를 하려고 한다. 각각 예제에 이슈사항이 있으면 미리 명시하고 추가되는 부분은 별도 표기를 하였으니 확인하면 된다.

Background & Borders

border-image

기존엔 border에 색상만 표현하는 게 가능했었지만 CSS3에서는 테두리에 이미지를 넣을 수 있고, 옵션을 통해 측면마다 다른 이미지 및 다양하게 표현할 수 있다.

Source
  1. div{
  2. padding:10px;
  3. border-width:27px;
  4. -webkit-border-image:url(img_border_image.png) 27 27 27 27 round;
  5. border-image:url(img_border_image.png) 27 27 27 27 round;
  6. }
View

속성사용방법
  1. border-image:url(①img_border_image.png) ②27 ③27 ④27 ⑤27 ⑥round;

① – 이미지 URL ② – top ③ – right ④ – bottom ⑤ – left ⑥ -이미지 반복 방법 (round:순환, stretch:늘이기, repeat:타일형식 반복, space:타일형식으로 반복되나 마지막 이미지 잘릴 수 있음)

좀 더 디테일하게 적용할 수 있는 속성들:

  • border-top-image:
  • border-right-image:
  • border-bottom-image:
  • border-left-image:
  • border-corner-image:
  • border-top-left-image:
  • border-top-right-image:
  • border-bottom-left-image:
  • border-bottom-right-image:
  • border-image-source: url(…);
  • border-image-slice:
  • border-image-widths:
  • border-image-outset:

border-radius

이젠 CSS만으로 테두리 모서리를 둥글게 표현이 가능하다. 파이 값을 조절 함으로써 둥근 모서리 크기를 조절 할 수 있다.

Source
  1. div{
  2. padding:10px;
  3. border:solid 5px #3399CC;
  4. -webkit-border-radius: 25px;
  5. border-radius: 25px;
  6. }
View

속성사용방법
  1. border-radius:①25px;

① – 파이 크기

좀 더 디테일하게 적용할 수 있는 속성들
  • border-top-radius:
  • border-right-radius:
  • border-bottom-radius:
  • border-left-radius:
  • border-top-left-radius:
  • border-top-right-radius:
  • border-bottom-left-radius:
  • border-bottom-right-radius:

Box-shadow

CSS만으로도 포토샵 같은 이미지편집툴 없이도 멋진 그라데이션이 들어가 있는 테두리 그림자를 설정 할 수 있다. 테두리 바깥쪽, 안쪽 모두 그림자를 생성할 수 있으며 그림자의 크기, 색상, 투명도를 조절할 수 있다.

Source
  1. div{
  2. padding:10px;
  3. border:#66ccff 3px solid;
  4. -webkit-box-shadow:rgba(106,198,247,0.3) -5px -5px 5px, rgba(106,198,247,0.7) 5px 5px 5px ;
  5. }
View

속성사용방법
  1. box-shadow:①rgba(106,198,247,0.3) ②-5px -5px 5px, ③rgba(106,198,247,0.7) ④5px 5px 5px ;
  • Left ① – RGB색상,투명도 ② – 가로,세로,blur 오프셋 크기
  • Right ③ – RGB색상,투명도 ④ – 가로,세로,blur 오프셋 크기

추가 옵션 inset – 안쪽으로 그림자 생김

Border-radius 와 Box-shadow 사용

두 속성을 같이 사용하여 둥근 모서리에 테두리 그림자 주는것도 간단한 CSS3 코드 몇 줄로 가능하다.

Source
  1. div{
  2. padding:10px;
  3. border-radius: 25px;
  4. -webkit-border-radius: 25px;
  5. border:#cbef38 3px solid;
  6. -webkit-box-shadow:rgba(152,208,138,0.3) -5px -5px 5px, rgba(152,208,138,0.7) 5px 5px 5px ;
  7. }
View

Background-size

배경이미지 크기를 임의적으로 설정할 수 있다. 하나의 요소에 여러 개의 배경이미지를 적용할 수도 있으며 각 배경이미지마다 별도의 크기를 지정해도 된다.

Source
  1. div{
  2. padding:10px;
  3. background-image:url(images/img_border_image.png);
  4. background-repeat: no-repeat;
  5. background-size: 50px 50px;
  6. -webkit-background-size: 50px 50px;
  7. }
View

원본이미지 

속성사용방법
  1. background-size: ①50px ②50px;

① – 배경이미지 가로사이즈 ② – 배경이미지 세로사이즈

좀 더 디테일하게 적용 할 수 있는 속성들
  • background-image:
  • background-color:
  • background-repeat:
  • background-position:
  • background-attachment:

Multiple background

하나의 element에 여러 개의 배경이미지를 적용할 수 있다.

Source
  1. div{
  2. padding:10px;
  3. background-image:url(images/img_border_image.png), url(images/img_border_image2.png);
  4. background-repeat: no-repeat;
  5. background-size: 50px 50px, 100px, 150px;
  6. -webkit-background-size: 50px 50px, 100px, 150px;
  7. }
속성사용방법
  1. background-image: ①url(...) ②url(...);
  2. background-size: ③50px ④50px ⑤100px ⑥150px;

① – 1번 배경이미지 URI ② – 2번 배경이미지 URI ③ – 2번 배경이미지 가로사이즈 ④ – 2번 배경이미지 세로사이즈
⑤ – 2번 배경이미지 가로사이즈 ⑥ – 2번 배경이미지 세로사이즈

rgba(R,G,B,A) / hsla(H,S,L,A)

기존 방식은 16진수 6자리를 이용하여 두자리씩 #00/00/00 나누어 표기하는 방식을 사용했었는데, CSS3에서는 rgba(R,G,B,A) 형식의 10진수(0~255)를 통한 RGB표현 그리고 A(투명도)를 표시하거나 HSLA(색상각도,채도,명도,투명도)로 표기할 수 있다.

Source
  1. div{
  2. padding:10px;
  3. color: rgba(0,0,0,1);
  4. color: hsla(180,0%,0%,1);
  5. background-color: rgba(150,100,80,.5);
  6. background-color: hsla(220,64%,69%,.5);
  7. }
View

속성사용방법
  1. color: ①rgba(0,0,0,1);
  2. color: ②hsla(180,0%,0%,1);
  3. background-color: ①rgba(150,100,80,.5);
  4. background-color: ②hsla(220,64%,69%,.5);

같은 기능을 하지만 두 가지 모두 테스트 해보고 차이점을 파악해 보자.
① – (R,G,B,투명도) ② – (색상각도,채도,명도,투명도)

좀 더 디테일하게 적용 할 수 있는 속성들
  • color:
  • background-color:

gradient

필자도 많이 기다려왔던 기능 중에 하나인 그라데이션 효과 기능이다. 배경에 그라데이션을 적용 할 수 있고 한가지 색상뿐 아니라 여러 색상표현이 가능하다.

Source
  1. div{
  2. padding:10px;
  3. border:solid 5px #66CCFF;
  4. border-radius: 20px;
  5. background:-webkit-gradient(linear, 80% 20%, 10% 20%, from(#ACE8E8), to(#E1F7F7));
  6. background:gradient(linear, 80% 20%, 10% 20%, from(#ACE8E8), to(#E1F7F7));
  7. }
View

속성사용방법
  1. background:gradient(①linear, ②80% 20%, 10% 20%, ③from(#ACE8E8), to(#E1F7F7));

① – 타입(linear,radial) ② – 시작위치, 끝위치
③ – 칼라 시작점, 칼라 끝점

opacity

요소에 투명도 값을 지정하는 속성이며 요소 전체에 효과가 적용된다.

Source
  1. div{
  2. padding:10px;
  3. opacity:.5;
  4. background:#<| class="nu0">777|>;
  5. }
View (적용 전)

View (적용 후)

속성사용방법
  1. color:opacity:.5;

① – 0~1까지 표시된 값으로 투명도를 표시한다.

Text(다음 연재에 타이포그래피 투토리얼을 진행 할 예정이라 간단하게 하고 넘김)

text-shadow

일반 텍스트 컨텐츠에 그림자를 입히는 속성이다.

Source
  1. div{
  2. text-shadow:4px 4px 4px #aaa;
  3. font-size:5em;
  4. font-family: arial;
  5. }

마크업에서 텍스트를 “Clear:both;” 라고 변경하고 진행하면 된다.

View
속성사용방법
  1. text-shadow: ①4px 4px ②4px ③#aaa;

① – 가로,세로 그림자거리 ② – 그림자 blur 크기
③ – 그림자 색상

text-overflow

텍스트 컨텐츠가 넘칠 때 줄여주거나 숨기거나 하는 속성이다.

Source
  1. div{
  2. padding:10px;
  3. width:300px;
  4. text-overflow:ellipsis;
  5. white-space:nowrap;
  6. overflow:hidden;
  7. border:5px solid #ccc;
  8. }
View

속성사용방법
  1. text-overflow:①ellipsis;
  2. white-space:②nowrap;
  3. overflow:③hidden;

① – 생략(ellipsis),자르기(clip) ② – 줄 바꿈을 하지 않기.
③ – 텍스트가 감싸고 있는 요소를 넘칠 때 숨김속성

resize

박스크기를 사용자가 임의로 조절할 수 있게 하는 속성이다. 가로,세로,모두 등의 설정이 가능하다.

Source
  1. div{
  2. padding:10px;
  3. text-overflow:ellipsis;
  4. white-space:nowrap;
  5. overflow:hidden;
  6. resize:horizontal;
  7. resize:vertical;
  8. border:5px solid #ccc;
  9. }
View

오른쪽 하단에 마우스를 클릭하여 드래그 할 수 있도록 2개의 사선이 그려져 있다.

속성사용방법
  1. resize:①horizontal;
  2. resize:②vertical;

① – 가로방향으로 사이즈 가변 됨 ② – 세로방향으로 사이즈 가변 됨 both는 가로,세로방향 모두로 가변 됨

multi-column layout

다중의 세로 열 레이아웃을 표현 할 수 있는 속성이다.

Source
  1. div{
  2. padding:10px;
  3. -webkit-column-count: <| class="nu0">3|>;
  4. -webkit-column-gap: 1.5em;
  5. -webkit-column-rule: 1px solid #ccc;
  6. border:5px solid #ccc;
  7. }
View

속성사용방법
  1. -webkit-column-count:①<| class="nu0">3|>;
  2. -webkit-column-gap:②1.5em;
  3. -webkit-column-rule:③1px solid #ccc;

① – 칼럼의 개수 ② – 칼럼 사이의 빈 여백 ③ – 칼럼 사이의 세로라인

마치며

지금까지 선택자의 종류 및 벤더별 확장속성 그리고 간단한 CSS3 속성들을 다루어 보았다. 연재 첫 페이지에도 언급했지만 CSS3는 아직 완성되어 스펙이 100% 정해진 것이 아니다. 그렇기 때문에 포괄적인 CSS3의 명세를 읽어보고 일단은 표현이 되는 브라우저에서 테스트 및 학습을 하는 게 좋을 것이다.

다음 연재에서는 “CSS3를 이용해 이쁜 타이포그래피 만들기”라는 주제를 가지고 폰트지정, 웹폰트 쓰는법, 텍스트를 표현하는 전반적인 부분에 대하여 다루고 그것들을 이용해 이미지 못지 않은 샘플 타이포그래피를 구성해보자.

끝으로 2011년 새해 첫 스타트를 해 튜토리얼 중심의 본 CSS3 연재를 시작하여 열정을 가지고 한다면 모두 자신이 이루고자 하는 성과보다 많은 것을 얻을 수 있을 것이다.


출처 : http://www.clearboth.org/css3_1_by_isdn386/


디폴트 설정 latin1에 의한 한글깨짐은 어김없이 APMSETUP7에서도 발생했다. OTL

 

하루동안의 삽집을 통해서 해결한 방법은 my.ini의 설정을 바꾸어 주고 이게 또 디렉토리 경로가 잘못되어 있어서 요렇게 바꿔서 복사해준다.

 

########################################

MySQL5 의 my.ini 설정 파일의 위치는


APM_Setup\Server\MySQL5\data


폴더가 아니라


APM_Setup\Server\MySQL5\

########################################

 

파일 수정내용은 이렇게 한다.

########################################

########################################

 

마지막으로 MYSQL 중지-> 재시작 하면 설정이 적용된다.

확인은 MYSQL 콘솔에서 아래와 같이 입력하면 결과를 보여준다.

 

########################################

########################################


출처 : http://blog.naver.com/delltin/90117427918

 


'공부 > SQL' 카테고리의 다른 글

mysql 사용자 계정 생성  (0) 2013.07.31
mysql 암호 재설정(mariaDB)  (0) 2013.07.25
mysql auto increment 값 설정, 초기화하기  (0) 2013.04.17
mysql 에서 php로 count가져오기  (1) 2013.01.04
mysql auto increment 초기화 하기  (0) 2013.01.03

float: left나 float: right된 element는 중앙 정렬이 되지 않습니다. 물론 

width를 줘서 margin를 이용하면 됩니다. 하지만 매번 width를 수정하는건 레알 귀찮습니다. 

간다하게 float: right/left, position: relative 그리고 left: +-50%를 이용해서 해결 할 수 있습니다. 욕덩이 6 에서도 잘 돌아가는걸 확인했습니다. 

예) 

#test { 

    float: right; 

    position: relative; 

    left: -50%; 


#test li { 

    float: left; 

    position: relative; 

    left: 50%; 

    

    border: 1px solid red; 


<ul id="test"> 

    <li>아이유</li> 

    <li>효느님</li> 

    <li>수지</li> 

    <li>리지</li> 

    <li>돼지</li> 

</ul> 


+ Recent posts