Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
ЛБ(ПРИС)_Файл-сервер_2016.doc
Скачиваний:
5
Добавлен:
17.06.2023
Размер:
5.49 Mб
Скачать
    1. Работа с данными

Для работы с данными понадобятся следующие компоненты, расположенные на вкладке Data Controls:

  • DBNavigator - необходим для перемещения по данным и работы с ними,

  • DBEdit - выводит текстовые и числовые поля записи для дальнейшего их редактирования,

  • DBLookupComboBox - выводит поля, являющиеся связанными к данному набору данных, то есть содержащие в своем значении код связи, в виде выпадающего списка,

  • DBLookupListBox - аналогичен компоненту DBLookupComboBox за тем исключением, что выводит данные не в выпадающий список, а в качестве строк поля.

Для удобства ввода даты воспользуемся компонентом DateTimePicker с вкладки Win32.

Изменим форму справочника «Сотрудники» таким образом, как показано на рисунке 17.

Рисунок 17 – Справочник «Сотрудники» с компонентами работы с данными

Значения свойств компонентов приведены в таблице 2.

Таблица 2 - Значения свойств компонентов

Компонент

Свойство

Значение

DBEdit0

CharCase

ecUpperCase

DataSource

DM.SotrDataSource

DataField

S_ID

Enabled

False

DBEdit1

CharCase

ecUpperCase

DataSource

DM.SotrDataSource

DataField

S_Fam

DBEdit2

CharCase

ecUpperCase

DataSource

DM.SotrDataSource

DataField

S_Name

DBEdit3

CharCase

ecUpperCase

DataSource

DM.SotrDataSource

DataField

S_Otch

DBEdit4

CharCase

ecUpperCase

DataSource

DM.SotrDataSource

DataField

S_DateBirth

Visible

False

DBLookupComboBox1

ListSource

DM.DolgnostDataSource

ListField

D_NAME

DataSource

DM.SotrDataSource

DataField

S_D_ID

KeyField

D_ID

DBLookupComboBox2

ListSource

DM.HobbyDataSource

ListField

H_Name

KeyField

H_ID

DBLookupListBox1

KeyField

S_H_ID

ListField

H_Name

ListSource

DM.Hobby_SotrDataSource

DBNavigator1

DataSource

DM.SotrDataSource

VisibleButtons

[nbPrior,nbNext,nbInsert,nbDelete,nbEdit,nbPost,nbCancel,nbRefresh]

Кнопки навигатора соответствуют действиям, представленным в таблице 3.

Таблица 3 – Кнопки и действия навигатора

nbFirst

Устанавливает курсор на первую запись в наборе

nbPrior

Устанавливает курсор на предыдущую запись по отношению к выбранной

nbNext

Устанавливает курсор на следующую запись по отношению к выбранной

nbLast

Устанавливает курсор на последнюю запись

nbInsert

Для вставки новой записи

nbDelete

Удаляет выбранную запись из набора

nbPost

Запоминание изменений, сделанных в режиме добавления или изменения записи

nbCancel

Отменяет сделанные изменения в записи

nbRefresh

Обновляет данные

Работать с кнопками навигатора можно точно так же, как с обычными стандартными кнопками. При этом можно использовать, например, события навигатора OnClick (возникает при щелчке «мышкой» по кнопке навигатора) и BeforeAction (возникает перед выполнением действия соответствующей кнопки навигатора).

Естественно, чтобы определить какая из кнопок навигатора была нажата надо в теле процедуры обработки вышеназванных событий использовать конструкцию:

If Button=nbDelete (или другая кнопка) then

begin

оператор1;

оператор2;

end;

Пример процедуры BeforeAction для формы «Хобби»:

procedure TS_HobbyForm.DBNavigator1BeforeAction(Sender: TObject;

Button: TNavigateBtn);

begin

ins:=false;

// Если нажата кнопка «Вставить», тогда

// передаём фокус в DBEdit1, если он доступен

if Button=nbInsert then

begin

ins:=true;

if DBEdit1.Enabled then DBEdit1.SetFocus;

end;

//Если нажата кнопка «Обновить», тогда проверяем признак редактирования

if (Button=nbRefresh) then CheckEdit;

end;

Примечание: При запуске программа выдаст ошибку, так как процедура проверки признака редактирования CheckEdit ещё не прописана. Можно либо пока закомментировать строчку, содержащую обращение к процедуре, либо прописать процедуру (см. пункт 7 и программный пример), что лучше для проверки правильности функционирования.

При окончании редактирования даты в компоненте DateTimePicker её значение необходимо передать в DBEdit4, который невидим пользователю. Также при смене активной записи необходимо из поля DBEdit4 передавать данные в DateTimePicker.

То есть, для события OnCloseUp компонента DateTimePicker необходимо записать следующее:

procedure TS_sotrudnikiForm.DateTimePicker1CloseUp(Sender: TObject);

begin

// Нажимаем на DBNavigator1 кнопку редактирования

DBNavigator1.BtnClick(nbEdit);

// Передаём дату в DBEdit4

DBEdit4.EditText:=datetostr(DateTimePicker1.Date);

end;

Вывод графических полей

Для вывода поля записи, содержащей картинку, поместим на форму компонент Panel с вкладки Standard, а на него – компонент Image с вкладки Additional.

Не забудьте подключить в раздел uses (interface) модули:

- ExtCtrls, который включает обработку класса TImage (а также класс TTimer, который понадобится в дальнейшем),

- JPEG, включающий обработку класса ТJPEGImage,

- ExtDlgs, содержащий обработку классов TOpenPictureDialog, TSavePictureDialog.

Кроме того, необходимо вставить еще один компонент DBEdit (со свойствами DataSourceDM.SotrDataSource и DataFieldPhoto).

Необходимо также с вкладки Dialogs на форму переместить компонент OpenPictureDialogue. В свойстве Filter данного компонента необходимо указать следующее:

Рисунок18 – Настройка свойства Filter компонента OpenPictureDialog

Далее для работы с картинками нам необходимо будет прописать две функции: GetFileFormat (функция определяет расширение загружаемой картинки) и ExtractFileNameEx (функция определяет имя картинки с расширением или без него).

Пропишем эти функции в модуле DataModule. Для этого в разделе var перед разделом implementation необходимо объявить эти функции, при этом переменную DBPath необходимо сделать глобальной, чтобы она была доступна и для других модулей (т.е. тоже объявить в этом разделе), но тогда из фукнции TDM.ADOConnection1BeforeConnect объявление этой переменной необходимо убрать (см. пример), т.е.:

var

DM: TDM;

DBPath:widestring; // путь к БД

function ExtractFileNameEx(FileName: string; ShowExtension: Boolean): string;

function GetFileFormat(St: string): string;

implementation

После объявления данных функций, прописываем программный код их выполнения:

function GetFileFormat(St: string): string; //функция, определяющая расширение графического файла с точкой //(например, .jpg)

//ВХОДНОЙ ПАРАМЕТР – имя картинки с расширением (например, Photo1.jpg)

var

z: integer;

n: byte;

begin

// с помощью цикла определим количество знаков до точки

// чтобы затем это количество знаков удалить из входной строки и получить расширение с точкой

for z := length(St) -1 downto 0 do //начиная с предпоследнего знака (т.к. точка не может находиться в конце) //двигаемся по направлению к началу строки, т.е. z с каждым циклом уменьшается на 1

if (St[z] = '.') then //если очередной символ является точкой, то прекращаем цикл и запоминаем число z

begin

n := z;

break; //прекращение цикла

end;

Delete(St, 1, n); //из исходной строки удалили найденное количество символов

Result := St;

end;

function ExtractFileNameEx(FileName: string; ShowExtension: Boolean): string;

//ВХОДНЫЕ ПАРАМЕТРЫ

//FileName - имя файла, которое надо обработать

//ShowExtension - если TRUE, то функция возвратит короткое имя файла

// (без полного пути доступа к нему), с расширением этого файла, иначе ,возвратит

// короткое имя файла, без расширения этого файла.

var

I: Integer;

S, S1: string;

begin

//Определяем длину полного имени файла

I := Length(FileName);

//Если длина FileName <> 0, то

if I <> 0 then

begin

//С конца имени параметра FileName ищем символ "\"

while (FileName[i] <> '\') and (i > 0) do

i := i - 1;

// Копируем в переменную S параметр FileName начиная после последнего

// "\", таким образом переменная S содержит имя файла с расширением, но без

// полного пути доступа к нему

S := Copy(FileName, i + 1, Length(FileName) - i);

i := Length(S);

//Если полученная S = '' то фукция возвращает ''

if i = 0 then

begin

Result := '';

Exit;

end;

//Иначе, получаем имя файла без расширения

while (S[i] <> '.') and (i > 0) do

i := i - 1;

//... и сохраням это имя файла в переменную s1

S1 := Copy(S, 1, i - 1);

//если s1='' то , возвращаем s1=s

if s1 = '' then

s1 := s;

//Если было передано указание функции возвращать имя файла с его

// расширением, то Result = s,

//если без расширения, то Result = s1

if ShowExtension = TRUE then

Result := s

else

Result := s1;

end

//Иначе функция возвращает ''

else

Result := '';

end;

Так как имя графического файла содержит в себе код записи, к которой он относится, то необходимо обеспечить такую обработку кода записи, чтобы независимо от количества знаков в этом коде, все картинки имели одну и ту же точность в имени (например, 9 знаков), для унифицированной их обработки. Для этого рассмотрим функцию

FORMAT

Функция Format форматирует строку в соответствии с указанными инструкциями. Формат фукнции:

function Format (const Format: string; const Args: array of const) : string;

Первым параметром функции выступает форматирующая строка. Это — обыч­ная текстовая строка, но в ней на нужных местах стоят специальные символы, которые определяют, какие и как туда будут подставлены параметры.

Второй параметр – список аргументов. Он и со­держит "вставляемые" в форматирующую строку параметры. Обратите внимение, что этот открытый массив имеет тип array of const. и в нем может передаваться переменное число разнотипных параметров (индексация элементов в массиве начинается с нуля).

Общий формат форматирования каждой подстроки следующий:

%[Index:][-][Width][.Precision]Type

где квадратные скобки относятся к дополнительным параметрам (т.е. необязательным).

Здесь:

[Index:] - поле индекса аргумента, позволяющее динамически изменить последовательность извлечения аргументов из массива.

[-] – признак выравнивания по левому краю, позволяет выравнивать строку не по правому краю (как по умолчанию), а по левому.

[Width] – поле ширины, устанавливает минимально допустимое количество символов в выводимой строке, если она короче, чем задано, происходит добавление пробелов слева (по умолчанию) до нужного размера.

[.Precision] – поле точности, которое играет разную роль в зависимости от того, с каким типом преобразования применяется.

В простых условиях (без всех вышеперечисленных дополнительных полей) каждые данные, форматирующие подстроку начинаются с % и заканчиваются индикатором типа данных:

d = Десятичное (целое число)

e = Научный (число с плавающей точкой)

f = Установленный

g = Обобщенный (преобразуется к одному из вышеперечисленных видов – выбирается тот, который обеспечивает более короткую запись)

m = Деньги

n = Число (плавающее)

p = Указатель

s = Строка

u = Десятичное число без знака

x = Шестнадцатеричный

Пример 1 (использование типов преобразования):

Функция Результат

---------------------------------------------------------------------------------------

Format('Decimal= %d', [-123]); Decimal = -123

Format('Exponent=%e',[12345.678]); Exponent = 1.23456780000000E+004

Format('Fixed = %f', [12345.678]); Fixed = 12345.68

Format('General= %g', [12345.678]); General = 12345.678

Format('Number= %n', [12345.678]); Number = 12,345,68

Format('Money = %m', [12345.678]); Money = ?12,345.68

Format('Pointer= %p', [addr(text)]); Pointer = 0069FC90

Format('String = %s', ['Hello']); String = Hello

Format('Unsigned decimal = %u', [123]); Unsigned decimal = 123

Format('Hexadecimal = %x', [140]); Hexadecimal = 8C

Пример 2 (использование индекса, ширины и значения точности):

Функция Результат('<','>' - не выводятся)

---------------------------------------------------------------------------------------

// использование поля ширины (добавляются три пробела слева)

Format('Padded decimal = <%7d>', [1234]) Padded decimal = < 1234>

// использование выравнивания по левому краю

Format('Justified decimal = <%-7d>', [1234]); Justified decimal = <1234 >

// значение точности вынуждает 0 дополнений к желательному размеру

ShowMessage(Format('0 padded decimal=<%.6d>', [1234])); 0 padded decimal = <001234>

// комбинация ширины и точности

Format('Width + precision = <%8.6d>', [1234]); Width+precision=< 001234>

// индексное значение – при достижении 4ого параметра подставляется элемент массива под индексом 1 (индексация с нуля)

Format('Reposition after 3 strings = %s %s %s %1:s %s', ['Zero', 'One', 'Two', 'Three']);

Reposition after 3 strings = Zero One Two One Two

Необходимо создать в папке DB папку PIC, как в примере, или собственную папку, но тогда изменить программный код в некоторых местах. В этой папке будут храниться картинки, имена которых будут соответствовать коду записи, а точность имени будет содержать 9 знаков.

Далее в событии OnDblClick компонента Image следует написать следующий программный код:

procedure TS_sotrudnikiForm.Image1DblClick(Sender: TObject);

var

SourceFileName:string;

FileFormat:string;

DestFullFileName:widestring;

{ SourceFileName - полное имя файла (<путь>/<файл>.<расш>) изображения, выбранного пользователем

FileFormat - расширение файла (.<расш>) изображения, выбранного пользователем

DestFullFileName - сгенерированное полное имя файла (<путь>/<файл>.<расш>)}

begin

if (OpenPictureDialog1.Execute) then

begin

Image1.Picture.LoadFromFile(OpenPictureDialog1.FileName);

SourceFileName:=DataModule.ExtractFileNameEx(OpenPictureDialog1.FileName,True);

FileFormat:= '.'+DataModule.GetFileFormat(SourceFileName);

DBEdit.Text:=FileFormat;

if (DBEdit0.Text<>'') then

begin

// нажимаем кнопку редактирования

// изменяем формат

DBNavigator1.BtnClick(nbEdit);

// если загруженное изображение иного формата, то удаляем предыдущее изображение из папки,

// иначе просто перезаписываем

if DBEdit.Text<>FileFormat then

DeleteFile(DataModule.DBPath+'PIC\'+Format('%.9d%s',[strtoint(DBEdit0.Text),DBEdit.Text]));

DBEdit.Text:=FileFormat;

DestFullFileName:=DataModule.DBPath+'PIC\'+Format('%.9d%s',[strtoint(DBEdit0.Text),FileFormat]);

Image1.Picture.SaveToFile(DestFullFileName);

end;

end;

end;

Далее для того, чтобы показывать картинки при перемещении по записям, отслеживать их наличие, необходимо в событии OnChange компонента DBEdit0 (содержащего код записи) прописать следующее:

Примечание: не забудьте создать и описать переменную ins и функцию chekEdit, а также на модуль данных добавить необходимые компоненты доступа к данным. ( Смотри Пример программы.)

procedure TS_sotrudnikiForm.DBEdit0Change(Sender: TObject);

var FileName:widestring;

begin

if (DBEdit0.Text<>'')and(not ins) then checkEdit;

if (DBEdit0.Text<>'') then

begin

FileName:=DataModule.DBPath+'PIC\'+Format('%.9d%s',[strtoint(DBEdit0.Text),DBEdit.Text]);

// если файл изображения НЕ СУЩЕСТВУЕТ, но ЗАДАННО расширение файла (новая запись),

// тогда сохраняем файл из компонента Image1 (если Picture пустое, т.е. файл не загружен, то сохранения не произойдет)

if (not FileExists(FileName))and(DBEdit.Text<>'') then

Image1.Picture.SaveToFile(FileName);

// если файл изображения СУЩЕСТВУЕТ и ЗАДАННО расширение файла

// тогда загружаем его в компонент Image1

if (FileExists(FileName))and(DBEdit.Text<>'') then

Image1.Picture.LoadFromFile(FileName)

else Image1.Picture:=nil;

DM.Hobby_SotrADOQuery.SQL.Text:=

'SELECT HOBBY_SOTR.S_H_ID, HOBBY_SOTR.S_ID, HOBBY_SOTR.H_ID, HOBBY.H_Name '+

'FROM HOBBY INNER JOIN HOBBY_SOTR ON HOBBY.H_ID = HOBBY_SOTR.H_ID '+

'WHERE (((HOBBY_SOTR.S_ID)='+DBEdit0.Text+'));';

DM.Hobby_SotrADOQuery.Open;

end;

end;

Для работы с картинками осталось обработать действия DBNavigator, для этого в событии OnClick необходимо записать следующее:

procedure TS_sotrudnikiForm.DBNavigator1Click(Sender: TObject;

Button: TNavigateBtn);

begin

// при редактированние существующей записи

// если кнопка принять и поля "код" и формат изобр. не пустые

// тогда сохраняем (перезаписываем) файл

if (Button=nbPost)and(DBEdit0.Text<>'')and(DBEdit.Text<>'') then

Image1.Picture.SaveToFile(DataModule.DBPath+'PIC\'+Format('%.9d%s',[strtoint(DBEdit0.Text),DBEdit.Text]));

end;

Событие BeforeAction компонента DBNavigator определяет его действия, которые выполняются после нажатия на DBNavigator, но до выполнения основных действий (OnClick).

В этом событии для кнопки nbInsert вставим следующее:

if Button=nbInsert then

begin

Image1.Picture:=nil;

end;

Т.е. перед тем, как вставить новую запись, освобождаем поле графического файла.

При нажатии на кнопку nbDelete необходимо удалить графический файл, соответствующий удаляемой записи, из специально созданной для хранения этих файлов папки:

if (Button=nbDelete) then

DeleteFile(DataModule.DBPath+'PIC\'+Format('%.9d%s',[strtoint(DBEdit0.Text),DBEdit.Text]));

Создание кнопок для работы с данными

В свойстве SQL компонента Hobby_SotrADOQuery будет храниться запрос на выборку всех записей из таблицы Hobby_Sotr по соответствующему коду текущего сотрудника, т.е. результатом запроса будут все хобби выбранного сотрудника. Данный запрос будет формироваться динамически при событии смены текущего сотрудника.

Так как действия с основным набором данных происходят при помощи навигатора, то кнопки “Добавить”, “Изменить”, “Удалить” нужны для работы со связанными таблицами, выводящимися на той же форме, что и основной набор данных (в примере эти кнопки обрабатывают таблицу HOBBY_SOTH, в то время, как навигатор обслуживает таблицу SOTR).

Выбор таблицы, которую будут обслуживать данные кнопки, осуществляется в соответствие с техническим заданием и с логикой работы системы.

При нажатии на кнопки «Добавить», «Изменить», «Удалить» необходимо выполнять следующие действия:

«Добавить»:

  • Осуществить проверку на добавление пустой записи (если запись пустая то свойство KeyValue компонента DBLookupComboBox2 будет иметь значение null);

  • Осуществить проверку на наличие подобной записи у данного сотрудника (в этом случае удобно использовать функцию Locate(const <Поле>: string; const <значение>: Variant; <опции>: TLocateOptions): Boolean;)

DM.Hobby_SotrADOQuery.Locate('H_ID',DBLookupComboBox2.KeyValue,[loPartialKey])

Проверка осуществляется в том запросе, который соответствует обрабатываемой таблице, т.е. в примере – DM.Hobby_SotrADOQuery.Locate. В скобках 1ый параметр – имя поля, по которому идет поиск (не обязательно ключевое), 2ой параметр – значение, с которым сравнивается значения этого поля, т.е. типы данных 1ого и 2ого параметра должны совпадать, необходимо также указать опции или их отсутствие (в квадратных скобках).

В случае обнаружения подобной записи в базе данных, выводится сообщение:

MessageDlg('Данное хобби уже внесено в список!',mtWarning, [mbOk], 0)

Рассмотрим функцию

MessageDlg

Функция MessageDlg используется для отображения сообщений пользователю. Эти сообщения могут быть информационными, предупреждающими и др. Даётся полный свободный выбор кнопок, которые пользователь может нажать, чтобы подтвердить диалог.

function MessageDlg ( const Message : string; DialogType : TMsgDlgType; Buttons : TMsgDlgButtons; HelpContext : Longint ) : Integer;

Значение DialogType может иметь одно из следующих перечисленных значений:

mtWarning - отображает символ восклицания

mtError - отображает красный "Х" (ошибка)

mtInformation - отображает "i" в круге

mtConfirmation - отображает знак вопроса

mtCustom - отображает только сообщение

Значение Buttons может быть одним из следующих перечисленных значений:

mbYes - кнопка "Yes"

mbNo - кнопка "No"

mbOK - кнопка "OK"

mbCancel - кнопка "Cancel"

mbAbort - кнопка "Abort"

mbRetry - кнопка "Retry"

mbIgnore - кнопка "Ignore"

mbAll - кнопка "All"

mbNoToAll - кнопка "No to all"

mbYesToAll - кнопка "Yes to all"

mbHelp - кнопка "Help"

Вы задаёте эти значения в квадратных скобках разделённых запятой. Delphi обеспечивает множество предопределенных комбинаций кнопок:

mbYesNoCancel = [mbYes, mbNO, mbCancel]

mbYesAllNoAllCancel = [mbYes, mbYesToAll, mbNo, mbNoToAll, mbCancel]

mbOKCancel = [mbOK, mbCancel]

mbAbortRetryCancel = [mbAbort, mbRetry, mbCancel]

mbAbortIgnore = [mbAbort, mbIgnore]

  • Сформировать и выполнить запрос на добавление записи в таблицу;

Для добавления новой строки в таблицу используется оператор SQL – запроса INSERT. Существует два варианта использования данного оператора:

  1. не определяются имена столбцов, т.к. вставляется новое значение в каждый столбец таблицы, но при этом количество значений должно точно соответствовать количеству столбцов таблицы. Синтаксис оператора:

INSERT INTO table_name VALUES (value1, value2, value3,...)

  1. определяются имена конкретных столбцов и значения, которые необходимо вставить именно в эти столбцы:

INSERT INTO table_name (column1, column2, column3,...) VALUES (value1, value2, value3,...)

В примере реализован 2ой способ:

DM.Hobby_SotrADOQuery.SQL.Text:= 'INSERT INTO HOBBY_SOTR ( S_ID, H_ID ) VALUES ('+DBEdit0.Text+','+inttostr (DBLookupComboBox2.KeyValue ) + ');';

Так как все переменные (VALUES) являются внешними параметрами (т.е. не являются заранее известными), то оформляются вставками в SQL-запрос с помощью “+”. Причем, если вставляются только цифры (как в примере), то апострофов достаточно, если же необходимо вставить текст, то необходимо добавить двойные кавычки, например:

'INSERT INTO Brands_Of_Cars(Brand) VALUES ("'+Cars.Edit2.Text+'");';

Здесь в таблицу Brands_Of_Cars, содержащую марки машин, в столбец Brand вставляется нововведенная марка в поле Edit2 (является текстом).

После вставки новой записи в таблицу исполняется, но не открывается данный запрос:

DM.Hobby_SotrADOQuery.ExecSQL;

  • Восстановить прежний запрос;

«Изменить»:

  • Осуществить проверку на изменение на пустую запись;

  • Осуществить проверку на наличие подобной записи у данного сотрудника;

Эту проверку можно также осуществлять с использованием функции Locate.

  • Сформировать и выполнить запрос на обновление записи таблицы;

Для обновления существующей записи в таблице используется оператор SQL-запроса UPDATE. Синтаксис оператора:

UPDATE table_name SET column1=value, column2=value2,... WHERE some_column=some_value

В примере:

DM.Hobby_SotrADOQuery.SQL.Text:= 'UPDATE HOBBY_SOTR SET H_ID='+inttostr(DBLookupComboBox2.KeyValue) WHERE S_H_ID='+inttostr( DBLookupListBox1.KeyValue)+';';

Оператор WHERE определяет, какую запись необходимо обновить, если пропустить этот оператор, обновлены будут все записи.

Так же, как и с функцией INSERT, если необходимо обновить числовые значения – апострофов будет достаточно, если же текстовые – необходимо добавить еще и двойные кавычки.

  • Восстановить прежний запрос;

«Удалить»

  • Сформировать и выполнить запрос на удаление записи из таблицы;

Для удаления записи из таблицы используется оператор SQL-запроса DELETE. Синтаксис оператора:

DELETE FROM table_name WHERE some_column=some_value

Оператор WHERE определяет какая запись/записи будут удалены. Если пропустить этот оператор, будут удалены все записи.

В примере:

DM.Hobby_SotrADOQuery.SQL.Text:= 'DELETE * FROM HOBBY_SOTR WHERE S_H_ID='+inttostr(DBLookupListBox1.KeyValue)+';';

  • Восстановить прежний запрос;